c8919dcad7eaff97622837bf21a2df0c59d7e742
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         
2523         var dom = move_card.el.dom;
2524         dom.parentNode.removeChild(dom);
2525         dom.style.width = ''; // clear with - which is set by drag.
2526         
2527         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2528             var cardel = next_to_card.el.dom;
2529             
2530             if (position == 'above' ) {
2531                 cardel.parentNode.insertBefore(dom, cardel);
2532             } else if (cardel.nextSibling) {
2533                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2534             } else {
2535                 cardel.parentNode.append(dom);
2536             }
2537         } else {
2538             // card container???
2539             this.containerEl.dom.append(dom);
2540         }
2541         
2542         //FIXME HANDLE card = true 
2543         
2544         // add this to the correct place in items.
2545         
2546         
2547         
2548         // remove Card from items.
2549         
2550         var old_parent = move_card.parent();
2551         
2552         old_parent.items = old_parent.items.filter(function(e) { return e != move_card });
2553         
2554         if (this.items.length) {
2555             var nitems = [];
2556             //Roo.log([info.items_n, info.position, this.items.length]);
2557             for (var i =0; i < this.items.length; i++) {
2558                 if (i == to_items_n && position == 'above') {
2559                     nitems.push(move_card);
2560                 }
2561                 nitems.push(this.items[i]);
2562                 if (i == to_items_n && position == 'below') {
2563                     nitems.push(move_card);
2564                 }
2565             }
2566             this.items = nitems;
2567             Roo.log(this.items);
2568         } else {
2569             this.items.push(move_card);
2570         }
2571         
2572         move_card.parentId = this.id;
2573         
2574         return true;
2575         
2576         
2577     },
2578     
2579     
2580     /**    Decide whether to drop above or below a View node. */
2581     getDropPoint : function(e, n, dd)
2582     {
2583         if (dd) {
2584              return false;
2585         }
2586         if (n == this.containerEl.dom) {
2587             return "above";
2588         }
2589         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2590         var c = t + (b - t) / 2;
2591         var y = Roo.lib.Event.getPageY(e);
2592         if(y <= c) {
2593             return "above";
2594         }else{
2595             return "below";
2596         }
2597     },
2598     onToggleCollapse : function(e)
2599         {
2600         if (this.collapsed) {
2601             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2602             this.collapsableEl.addClass('show');
2603             this.collapsed = false;
2604             return;
2605         }
2606         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2607         this.collapsableEl.removeClass('show');
2608         this.collapsed = true;
2609         
2610     
2611     },
2612     
2613     onToggleRotate : function(e)
2614     {
2615         this.collapsableEl.removeClass('show');
2616         this.footerEl.removeClass('d-none');
2617         this.el.removeClass('roo-card-rotated');
2618         this.el.removeClass('d-none');
2619         if (this.rotated) {
2620             
2621             this.collapsableEl.addClass('show');
2622             this.rotated = false;
2623             this.fireEvent('rotate', this, this.rotated);
2624             return;
2625         }
2626         this.el.addClass('roo-card-rotated');
2627         this.footerEl.addClass('d-none');
2628         this.el.select('.roo-collapsable').removeClass('show');
2629         
2630         this.rotated = true;
2631         this.fireEvent('rotate', this, this.rotated);
2632     
2633     },
2634     
2635     dropPlaceHolder: function (action, info, data)
2636     {
2637         if (this.dropEl === false) {
2638             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2639             cls : 'd-none'
2640             },true);
2641         }
2642         this.dropEl.removeClass(['d-none', 'd-block']);        
2643         if (action == 'hide') {
2644             
2645             this.dropEl.addClass('d-none');
2646             return;
2647         }
2648         // FIXME - info.card == true!!!
2649         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2650         
2651         if (info.card !== true) {
2652             var cardel = info.card.el.dom;
2653             
2654             if (info.position == 'above') {
2655                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2656             } else if (cardel.nextSibling) {
2657                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2658             } else {
2659                 cardel.parentNode.append(this.dropEl.dom);
2660             }
2661         } else {
2662             // card container???
2663             this.containerEl.dom.append(this.dropEl.dom);
2664         }
2665         
2666         this.dropEl.addClass('d-block roo-card-dropzone');
2667         
2668         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2669         
2670         
2671     
2672     
2673     
2674     },
2675     setHeaderText: function(html)
2676     {
2677         this.headerContainerEl.dom.innerHTML = html;
2678     }
2679
2680     
2681 });
2682
2683 /*
2684  * - LGPL
2685  *
2686  * Card header - holder for the card header elements.
2687  * 
2688  */
2689
2690 /**
2691  * @class Roo.bootstrap.CardHeader
2692  * @extends Roo.bootstrap.Element
2693  * Bootstrap CardHeader class
2694  * @constructor
2695  * Create a new Card Header - that you can embed children into
2696  * @param {Object} config The config object
2697  */
2698
2699 Roo.bootstrap.CardHeader = function(config){
2700     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2701 };
2702
2703 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2704     
2705     
2706     container_method : 'getCardHeader' 
2707     
2708      
2709     
2710     
2711    
2712 });
2713
2714  
2715
2716  /*
2717  * - LGPL
2718  *
2719  * Card footer - holder for the card footer elements.
2720  * 
2721  */
2722
2723 /**
2724  * @class Roo.bootstrap.CardFooter
2725  * @extends Roo.bootstrap.Element
2726  * Bootstrap CardFooter class
2727  * @constructor
2728  * Create a new Card Footer - that you can embed children into
2729  * @param {Object} config The config object
2730  */
2731
2732 Roo.bootstrap.CardFooter = function(config){
2733     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2734 };
2735
2736 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2737     
2738     
2739     container_method : 'getCardFooter' 
2740     
2741      
2742     
2743     
2744    
2745 });
2746
2747  
2748
2749  /*
2750  * - LGPL
2751  *
2752  * Card header - holder for the card header elements.
2753  * 
2754  */
2755
2756 /**
2757  * @class Roo.bootstrap.CardImageTop
2758  * @extends Roo.bootstrap.Element
2759  * Bootstrap CardImageTop class
2760  * @constructor
2761  * Create a new Card Image Top container
2762  * @param {Object} config The config object
2763  */
2764
2765 Roo.bootstrap.CardImageTop = function(config){
2766     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2767 };
2768
2769 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2770     
2771    
2772     container_method : 'getCardImageTop' 
2773     
2774      
2775     
2776    
2777 });
2778
2779  
2780
2781  /*
2782  * - LGPL
2783  *
2784  * image
2785  * 
2786  */
2787
2788
2789 /**
2790  * @class Roo.bootstrap.Img
2791  * @extends Roo.bootstrap.Component
2792  * Bootstrap Img class
2793  * @cfg {Boolean} imgResponsive false | true
2794  * @cfg {String} border rounded | circle | thumbnail
2795  * @cfg {String} src image source
2796  * @cfg {String} alt image alternative text
2797  * @cfg {String} href a tag href
2798  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2799  * @cfg {String} xsUrl xs image source
2800  * @cfg {String} smUrl sm image source
2801  * @cfg {String} mdUrl md image source
2802  * @cfg {String} lgUrl lg image source
2803  * 
2804  * @constructor
2805  * Create a new Input
2806  * @param {Object} config The config object
2807  */
2808
2809 Roo.bootstrap.Img = function(config){
2810     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2811     
2812     this.addEvents({
2813         // img events
2814         /**
2815          * @event click
2816          * The img click event for the img.
2817          * @param {Roo.EventObject} e
2818          */
2819         "click" : true
2820     });
2821 };
2822
2823 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2824     
2825     imgResponsive: true,
2826     border: '',
2827     src: 'about:blank',
2828     href: false,
2829     target: false,
2830     xsUrl: '',
2831     smUrl: '',
2832     mdUrl: '',
2833     lgUrl: '',
2834
2835     getAutoCreate : function()
2836     {   
2837         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2838             return this.createSingleImg();
2839         }
2840         
2841         var cfg = {
2842             tag: 'div',
2843             cls: 'roo-image-responsive-group',
2844             cn: []
2845         };
2846         var _this = this;
2847         
2848         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2849             
2850             if(!_this[size + 'Url']){
2851                 return;
2852             }
2853             
2854             var img = {
2855                 tag: 'img',
2856                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2857                 html: _this.html || cfg.html,
2858                 src: _this[size + 'Url']
2859             };
2860             
2861             img.cls += ' roo-image-responsive-' + size;
2862             
2863             var s = ['xs', 'sm', 'md', 'lg'];
2864             
2865             s.splice(s.indexOf(size), 1);
2866             
2867             Roo.each(s, function(ss){
2868                 img.cls += ' hidden-' + ss;
2869             });
2870             
2871             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2872                 cfg.cls += ' img-' + _this.border;
2873             }
2874             
2875             if(_this.alt){
2876                 cfg.alt = _this.alt;
2877             }
2878             
2879             if(_this.href){
2880                 var a = {
2881                     tag: 'a',
2882                     href: _this.href,
2883                     cn: [
2884                         img
2885                     ]
2886                 };
2887
2888                 if(this.target){
2889                     a.target = _this.target;
2890                 }
2891             }
2892             
2893             cfg.cn.push((_this.href) ? a : img);
2894             
2895         });
2896         
2897         return cfg;
2898     },
2899     
2900     createSingleImg : function()
2901     {
2902         var cfg = {
2903             tag: 'img',
2904             cls: (this.imgResponsive) ? 'img-responsive' : '',
2905             html : null,
2906             src : 'about:blank'  // just incase src get's set to undefined?!?
2907         };
2908         
2909         cfg.html = this.html || cfg.html;
2910         
2911         cfg.src = this.src || cfg.src;
2912         
2913         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2914             cfg.cls += ' img-' + this.border;
2915         }
2916         
2917         if(this.alt){
2918             cfg.alt = this.alt;
2919         }
2920         
2921         if(this.href){
2922             var a = {
2923                 tag: 'a',
2924                 href: this.href,
2925                 cn: [
2926                     cfg
2927                 ]
2928             };
2929             
2930             if(this.target){
2931                 a.target = this.target;
2932             }
2933             
2934         }
2935         
2936         return (this.href) ? a : cfg;
2937     },
2938     
2939     initEvents: function() 
2940     {
2941         if(!this.href){
2942             this.el.on('click', this.onClick, this);
2943         }
2944         
2945     },
2946     
2947     onClick : function(e)
2948     {
2949         Roo.log('img onclick');
2950         this.fireEvent('click', this, e);
2951     },
2952     /**
2953      * Sets the url of the image - used to update it
2954      * @param {String} url the url of the image
2955      */
2956     
2957     setSrc : function(url)
2958     {
2959         this.src =  url;
2960         
2961         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2962             this.el.dom.src =  url;
2963             return;
2964         }
2965         
2966         this.el.select('img', true).first().dom.src =  url;
2967     }
2968     
2969     
2970    
2971 });
2972
2973  /*
2974  * - LGPL
2975  *
2976  * image
2977  * 
2978  */
2979
2980
2981 /**
2982  * @class Roo.bootstrap.Link
2983  * @extends Roo.bootstrap.Component
2984  * Bootstrap Link Class
2985  * @cfg {String} alt image alternative text
2986  * @cfg {String} href a tag href
2987  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2988  * @cfg {String} html the content of the link.
2989  * @cfg {String} anchor name for the anchor link
2990  * @cfg {String} fa - favicon
2991
2992  * @cfg {Boolean} preventDefault (true | false) default false
2993
2994  * 
2995  * @constructor
2996  * Create a new Input
2997  * @param {Object} config The config object
2998  */
2999
3000 Roo.bootstrap.Link = function(config){
3001     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3002     
3003     this.addEvents({
3004         // img events
3005         /**
3006          * @event click
3007          * The img click event for the img.
3008          * @param {Roo.EventObject} e
3009          */
3010         "click" : true
3011     });
3012 };
3013
3014 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3015     
3016     href: false,
3017     target: false,
3018     preventDefault: false,
3019     anchor : false,
3020     alt : false,
3021     fa: false,
3022
3023
3024     getAutoCreate : function()
3025     {
3026         var html = this.html || '';
3027         
3028         if (this.fa !== false) {
3029             html = '<i class="fa fa-' + this.fa + '"></i>';
3030         }
3031         var cfg = {
3032             tag: 'a'
3033         };
3034         // anchor's do not require html/href...
3035         if (this.anchor === false) {
3036             cfg.html = html;
3037             cfg.href = this.href || '#';
3038         } else {
3039             cfg.name = this.anchor;
3040             if (this.html !== false || this.fa !== false) {
3041                 cfg.html = html;
3042             }
3043             if (this.href !== false) {
3044                 cfg.href = this.href;
3045             }
3046         }
3047         
3048         if(this.alt !== false){
3049             cfg.alt = this.alt;
3050         }
3051         
3052         
3053         if(this.target !== false) {
3054             cfg.target = this.target;
3055         }
3056         
3057         return cfg;
3058     },
3059     
3060     initEvents: function() {
3061         
3062         if(!this.href || this.preventDefault){
3063             this.el.on('click', this.onClick, this);
3064         }
3065     },
3066     
3067     onClick : function(e)
3068     {
3069         if(this.preventDefault){
3070             e.preventDefault();
3071         }
3072         //Roo.log('img onclick');
3073         this.fireEvent('click', this, e);
3074     }
3075    
3076 });
3077
3078  /*
3079  * - LGPL
3080  *
3081  * header
3082  * 
3083  */
3084
3085 /**
3086  * @class Roo.bootstrap.Header
3087  * @extends Roo.bootstrap.Component
3088  * Bootstrap Header class
3089  * @cfg {String} html content of header
3090  * @cfg {Number} level (1|2|3|4|5|6) default 1
3091  * 
3092  * @constructor
3093  * Create a new Header
3094  * @param {Object} config The config object
3095  */
3096
3097
3098 Roo.bootstrap.Header  = function(config){
3099     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3100 };
3101
3102 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3103     
3104     //href : false,
3105     html : false,
3106     level : 1,
3107     
3108     
3109     
3110     getAutoCreate : function(){
3111         
3112         
3113         
3114         var cfg = {
3115             tag: 'h' + (1 *this.level),
3116             html: this.html || ''
3117         } ;
3118         
3119         return cfg;
3120     }
3121    
3122 });
3123
3124  
3125
3126  /*
3127  * Based on:
3128  * Ext JS Library 1.1.1
3129  * Copyright(c) 2006-2007, Ext JS, LLC.
3130  *
3131  * Originally Released Under LGPL - original licence link has changed is not relivant.
3132  *
3133  * Fork - LGPL
3134  * <script type="text/javascript">
3135  */
3136  
3137 /**
3138  * @class Roo.bootstrap.MenuMgr
3139  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3140  * @singleton
3141  */
3142 Roo.bootstrap.MenuMgr = function(){
3143    var menus, active, groups = {}, attached = false, lastShow = new Date();
3144
3145    // private - called when first menu is created
3146    function init(){
3147        menus = {};
3148        active = new Roo.util.MixedCollection();
3149        Roo.get(document).addKeyListener(27, function(){
3150            if(active.length > 0){
3151                hideAll();
3152            }
3153        });
3154    }
3155
3156    // private
3157    function hideAll(){
3158        if(active && active.length > 0){
3159            var c = active.clone();
3160            c.each(function(m){
3161                m.hide();
3162            });
3163        }
3164    }
3165
3166    // private
3167    function onHide(m){
3168        active.remove(m);
3169        if(active.length < 1){
3170            Roo.get(document).un("mouseup", onMouseDown);
3171             
3172            attached = false;
3173        }
3174    }
3175
3176    // private
3177    function onShow(m){
3178        var last = active.last();
3179        lastShow = new Date();
3180        active.add(m);
3181        if(!attached){
3182           Roo.get(document).on("mouseup", onMouseDown);
3183            
3184            attached = true;
3185        }
3186        if(m.parentMenu){
3187           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3188           m.parentMenu.activeChild = m;
3189        }else if(last && last.isVisible()){
3190           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3191        }
3192    }
3193
3194    // private
3195    function onBeforeHide(m){
3196        if(m.activeChild){
3197            m.activeChild.hide();
3198        }
3199        if(m.autoHideTimer){
3200            clearTimeout(m.autoHideTimer);
3201            delete m.autoHideTimer;
3202        }
3203    }
3204
3205    // private
3206    function onBeforeShow(m){
3207        var pm = m.parentMenu;
3208        if(!pm && !m.allowOtherMenus){
3209            hideAll();
3210        }else if(pm && pm.activeChild && active != m){
3211            pm.activeChild.hide();
3212        }
3213    }
3214
3215    // private this should really trigger on mouseup..
3216    function onMouseDown(e){
3217         Roo.log("on Mouse Up");
3218         
3219         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3220             Roo.log("MenuManager hideAll");
3221             hideAll();
3222             e.stopEvent();
3223         }
3224         
3225         
3226    }
3227
3228    // private
3229    function onBeforeCheck(mi, state){
3230        if(state){
3231            var g = groups[mi.group];
3232            for(var i = 0, l = g.length; i < l; i++){
3233                if(g[i] != mi){
3234                    g[i].setChecked(false);
3235                }
3236            }
3237        }
3238    }
3239
3240    return {
3241
3242        /**
3243         * Hides all menus that are currently visible
3244         */
3245        hideAll : function(){
3246             hideAll();  
3247        },
3248
3249        // private
3250        register : function(menu){
3251            if(!menus){
3252                init();
3253            }
3254            menus[menu.id] = menu;
3255            menu.on("beforehide", onBeforeHide);
3256            menu.on("hide", onHide);
3257            menu.on("beforeshow", onBeforeShow);
3258            menu.on("show", onShow);
3259            var g = menu.group;
3260            if(g && menu.events["checkchange"]){
3261                if(!groups[g]){
3262                    groups[g] = [];
3263                }
3264                groups[g].push(menu);
3265                menu.on("checkchange", onCheck);
3266            }
3267        },
3268
3269         /**
3270          * Returns a {@link Roo.menu.Menu} object
3271          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3272          * be used to generate and return a new Menu instance.
3273          */
3274        get : function(menu){
3275            if(typeof menu == "string"){ // menu id
3276                return menus[menu];
3277            }else if(menu.events){  // menu instance
3278                return menu;
3279            }
3280            /*else if(typeof menu.length == 'number'){ // array of menu items?
3281                return new Roo.bootstrap.Menu({items:menu});
3282            }else{ // otherwise, must be a config
3283                return new Roo.bootstrap.Menu(menu);
3284            }
3285            */
3286            return false;
3287        },
3288
3289        // private
3290        unregister : function(menu){
3291            delete menus[menu.id];
3292            menu.un("beforehide", onBeforeHide);
3293            menu.un("hide", onHide);
3294            menu.un("beforeshow", onBeforeShow);
3295            menu.un("show", onShow);
3296            var g = menu.group;
3297            if(g && menu.events["checkchange"]){
3298                groups[g].remove(menu);
3299                menu.un("checkchange", onCheck);
3300            }
3301        },
3302
3303        // private
3304        registerCheckable : function(menuItem){
3305            var g = menuItem.group;
3306            if(g){
3307                if(!groups[g]){
3308                    groups[g] = [];
3309                }
3310                groups[g].push(menuItem);
3311                menuItem.on("beforecheckchange", onBeforeCheck);
3312            }
3313        },
3314
3315        // private
3316        unregisterCheckable : function(menuItem){
3317            var g = menuItem.group;
3318            if(g){
3319                groups[g].remove(menuItem);
3320                menuItem.un("beforecheckchange", onBeforeCheck);
3321            }
3322        }
3323    };
3324 }();/*
3325  * - LGPL
3326  *
3327  * menu
3328  * 
3329  */
3330
3331 /**
3332  * @class Roo.bootstrap.Menu
3333  * @extends Roo.bootstrap.Component
3334  * Bootstrap Menu class - container for MenuItems
3335  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3336  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3337  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3338  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3339  * 
3340  * @constructor
3341  * Create a new Menu
3342  * @param {Object} config The config object
3343  */
3344
3345
3346 Roo.bootstrap.Menu = function(config){
3347     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3348     if (this.registerMenu && this.type != 'treeview')  {
3349         Roo.bootstrap.MenuMgr.register(this);
3350     }
3351     
3352     
3353     this.addEvents({
3354         /**
3355          * @event beforeshow
3356          * Fires before this menu is displayed (return false to block)
3357          * @param {Roo.menu.Menu} this
3358          */
3359         beforeshow : true,
3360         /**
3361          * @event beforehide
3362          * Fires before this menu is hidden (return false to block)
3363          * @param {Roo.menu.Menu} this
3364          */
3365         beforehide : true,
3366         /**
3367          * @event show
3368          * Fires after this menu is displayed
3369          * @param {Roo.menu.Menu} this
3370          */
3371         show : true,
3372         /**
3373          * @event hide
3374          * Fires after this menu is hidden
3375          * @param {Roo.menu.Menu} this
3376          */
3377         hide : true,
3378         /**
3379          * @event click
3380          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3381          * @param {Roo.menu.Menu} this
3382          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3383          * @param {Roo.EventObject} e
3384          */
3385         click : true,
3386         /**
3387          * @event mouseover
3388          * Fires when the mouse is hovering over this menu
3389          * @param {Roo.menu.Menu} this
3390          * @param {Roo.EventObject} e
3391          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3392          */
3393         mouseover : true,
3394         /**
3395          * @event mouseout
3396          * Fires when the mouse exits this menu
3397          * @param {Roo.menu.Menu} this
3398          * @param {Roo.EventObject} e
3399          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3400          */
3401         mouseout : true,
3402         /**
3403          * @event itemclick
3404          * Fires when a menu item contained in this menu is clicked
3405          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3406          * @param {Roo.EventObject} e
3407          */
3408         itemclick: true
3409     });
3410     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3411 };
3412
3413 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3414     
3415    /// html : false,
3416     //align : '',
3417     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3418     type: false,
3419     /**
3420      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3421      */
3422     registerMenu : true,
3423     
3424     menuItems :false, // stores the menu items..
3425     
3426     hidden:true,
3427         
3428     parentMenu : false,
3429     
3430     stopEvent : true,
3431     
3432     isLink : false,
3433     
3434     getChildContainer : function() {
3435         return this.el;  
3436     },
3437     
3438     getAutoCreate : function(){
3439          
3440         //if (['right'].indexOf(this.align)!==-1) {
3441         //    cfg.cn[1].cls += ' pull-right'
3442         //}
3443         
3444         
3445         var cfg = {
3446             tag : 'ul',
3447             cls : 'dropdown-menu' ,
3448             style : 'z-index:1000'
3449             
3450         };
3451         
3452         if (this.type === 'submenu') {
3453             cfg.cls = 'submenu active';
3454         }
3455         if (this.type === 'treeview') {
3456             cfg.cls = 'treeview-menu';
3457         }
3458         
3459         return cfg;
3460     },
3461     initEvents : function() {
3462         
3463        // Roo.log("ADD event");
3464        // Roo.log(this.triggerEl.dom);
3465         
3466         this.triggerEl.on('click', this.onTriggerClick, this);
3467         
3468         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3469         
3470         
3471         if (this.triggerEl.hasClass('nav-item')) {
3472             // dropdown toggle on the 'a' in BS4?
3473             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3474         } else {
3475             this.triggerEl.addClass('dropdown-toggle');
3476         }
3477         if (Roo.isTouch) {
3478             this.el.on('touchstart'  , this.onTouch, this);
3479         }
3480         this.el.on('click' , this.onClick, this);
3481
3482         this.el.on("mouseover", this.onMouseOver, this);
3483         this.el.on("mouseout", this.onMouseOut, this);
3484         
3485     },
3486     
3487     findTargetItem : function(e)
3488     {
3489         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3490         if(!t){
3491             return false;
3492         }
3493         //Roo.log(t);         Roo.log(t.id);
3494         if(t && t.id){
3495             //Roo.log(this.menuitems);
3496             return this.menuitems.get(t.id);
3497             
3498             //return this.items.get(t.menuItemId);
3499         }
3500         
3501         return false;
3502     },
3503     
3504     onTouch : function(e) 
3505     {
3506         Roo.log("menu.onTouch");
3507         //e.stopEvent(); this make the user popdown broken
3508         this.onClick(e);
3509     },
3510     
3511     onClick : function(e)
3512     {
3513         Roo.log("menu.onClick");
3514         
3515         var t = this.findTargetItem(e);
3516         if(!t || t.isContainer){
3517             return;
3518         }
3519         Roo.log(e);
3520         /*
3521         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3522             if(t == this.activeItem && t.shouldDeactivate(e)){
3523                 this.activeItem.deactivate();
3524                 delete this.activeItem;
3525                 return;
3526             }
3527             if(t.canActivate){
3528                 this.setActiveItem(t, true);
3529             }
3530             return;
3531             
3532             
3533         }
3534         */
3535        
3536         Roo.log('pass click event');
3537         
3538         t.onClick(e);
3539         
3540         this.fireEvent("click", this, t, e);
3541         
3542         var _this = this;
3543         
3544         if(!t.href.length || t.href == '#'){
3545             (function() { _this.hide(); }).defer(100);
3546         }
3547         
3548     },
3549     
3550     onMouseOver : function(e){
3551         var t  = this.findTargetItem(e);
3552         //Roo.log(t);
3553         //if(t){
3554         //    if(t.canActivate && !t.disabled){
3555         //        this.setActiveItem(t, true);
3556         //    }
3557         //}
3558         
3559         this.fireEvent("mouseover", this, e, t);
3560     },
3561     isVisible : function(){
3562         return !this.hidden;
3563     },
3564     onMouseOut : function(e){
3565         var t  = this.findTargetItem(e);
3566         
3567         //if(t ){
3568         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3569         //        this.activeItem.deactivate();
3570         //        delete this.activeItem;
3571         //    }
3572         //}
3573         this.fireEvent("mouseout", this, e, t);
3574     },
3575     
3576     
3577     /**
3578      * Displays this menu relative to another element
3579      * @param {String/HTMLElement/Roo.Element} element The element to align to
3580      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3581      * the element (defaults to this.defaultAlign)
3582      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3583      */
3584     show : function(el, pos, parentMenu)
3585     {
3586         if (false === this.fireEvent("beforeshow", this)) {
3587             Roo.log("show canceled");
3588             return;
3589         }
3590         this.parentMenu = parentMenu;
3591         if(!this.el){
3592             this.render();
3593         }
3594         
3595         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3596     },
3597      /**
3598      * Displays this menu at a specific xy position
3599      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3600      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3601      */
3602     showAt : function(xy, parentMenu, /* private: */_e){
3603         this.parentMenu = parentMenu;
3604         if(!this.el){
3605             this.render();
3606         }
3607         if(_e !== false){
3608             this.fireEvent("beforeshow", this);
3609             //xy = this.el.adjustForConstraints(xy);
3610         }
3611         
3612         //this.el.show();
3613         this.hideMenuItems();
3614         this.hidden = false;
3615         this.triggerEl.addClass('open');
3616         this.el.addClass('show');
3617         
3618         // reassign x when hitting right
3619         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3620             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3621         }
3622         
3623         // reassign y when hitting bottom
3624         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3625             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3626         }
3627         
3628         // but the list may align on trigger left or trigger top... should it be a properity?
3629         
3630         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3631             this.el.setXY(xy);
3632         }
3633         
3634         this.focus();
3635         this.fireEvent("show", this);
3636     },
3637     
3638     focus : function(){
3639         return;
3640         if(!this.hidden){
3641             this.doFocus.defer(50, this);
3642         }
3643     },
3644
3645     doFocus : function(){
3646         if(!this.hidden){
3647             this.focusEl.focus();
3648         }
3649     },
3650
3651     /**
3652      * Hides this menu and optionally all parent menus
3653      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3654      */
3655     hide : function(deep)
3656     {
3657         if (false === this.fireEvent("beforehide", this)) {
3658             Roo.log("hide canceled");
3659             return;
3660         }
3661         this.hideMenuItems();
3662         if(this.el && this.isVisible()){
3663            
3664             if(this.activeItem){
3665                 this.activeItem.deactivate();
3666                 this.activeItem = null;
3667             }
3668             this.triggerEl.removeClass('open');;
3669             this.el.removeClass('show');
3670             this.hidden = true;
3671             this.fireEvent("hide", this);
3672         }
3673         if(deep === true && this.parentMenu){
3674             this.parentMenu.hide(true);
3675         }
3676     },
3677     
3678     onTriggerClick : function(e)
3679     {
3680         Roo.log('trigger click');
3681         
3682         var target = e.getTarget();
3683         
3684         Roo.log(target.nodeName.toLowerCase());
3685         
3686         if(target.nodeName.toLowerCase() === 'i'){
3687             e.preventDefault();
3688         }
3689         
3690     },
3691     
3692     onTriggerPress  : function(e)
3693     {
3694         Roo.log('trigger press');
3695         //Roo.log(e.getTarget());
3696        // Roo.log(this.triggerEl.dom);
3697        
3698         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3699         var pel = Roo.get(e.getTarget());
3700         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3701             Roo.log('is treeview or dropdown?');
3702             return;
3703         }
3704         
3705         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3706             return;
3707         }
3708         
3709         if (this.isVisible()) {
3710             Roo.log('hide');
3711             this.hide();
3712         } else {
3713             Roo.log('show');
3714             this.show(this.triggerEl, '?', false);
3715         }
3716         
3717         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3718             e.stopEvent();
3719         }
3720         
3721     },
3722        
3723     
3724     hideMenuItems : function()
3725     {
3726         Roo.log("hide Menu Items");
3727         if (!this.el) { 
3728             return;
3729         }
3730         
3731         this.el.select('.open',true).each(function(aa) {
3732             
3733             aa.removeClass('open');
3734          
3735         });
3736     },
3737     addxtypeChild : function (tree, cntr) {
3738         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3739           
3740         this.menuitems.add(comp);
3741         return comp;
3742
3743     },
3744     getEl : function()
3745     {
3746         Roo.log(this.el);
3747         return this.el;
3748     },
3749     
3750     clear : function()
3751     {
3752         this.getEl().dom.innerHTML = '';
3753         this.menuitems.clear();
3754     }
3755 });
3756
3757  
3758  /*
3759  * - LGPL
3760  *
3761  * menu item
3762  * 
3763  */
3764
3765
3766 /**
3767  * @class Roo.bootstrap.MenuItem
3768  * @extends Roo.bootstrap.Component
3769  * Bootstrap MenuItem class
3770  * @cfg {String} html the menu label
3771  * @cfg {String} href the link
3772  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3773  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3774  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3775  * @cfg {String} fa favicon to show on left of menu item.
3776  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3777  * 
3778  * 
3779  * @constructor
3780  * Create a new MenuItem
3781  * @param {Object} config The config object
3782  */
3783
3784
3785 Roo.bootstrap.MenuItem = function(config){
3786     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3787     this.addEvents({
3788         // raw events
3789         /**
3790          * @event click
3791          * The raw click event for the entire grid.
3792          * @param {Roo.bootstrap.MenuItem} this
3793          * @param {Roo.EventObject} e
3794          */
3795         "click" : true
3796     });
3797 };
3798
3799 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3800     
3801     href : false,
3802     html : false,
3803     preventDefault: false,
3804     isContainer : false,
3805     active : false,
3806     fa: false,
3807     
3808     getAutoCreate : function(){
3809         
3810         if(this.isContainer){
3811             return {
3812                 tag: 'li',
3813                 cls: 'dropdown-menu-item '
3814             };
3815         }
3816         var ctag = {
3817             tag: 'span',
3818             html: 'Link'
3819         };
3820         
3821         var anc = {
3822             tag : 'a',
3823             cls : 'dropdown-item',
3824             href : '#',
3825             cn : [  ]
3826         };
3827         
3828         if (this.fa !== false) {
3829             anc.cn.push({
3830                 tag : 'i',
3831                 cls : 'fa fa-' + this.fa
3832             });
3833         }
3834         
3835         anc.cn.push(ctag);
3836         
3837         
3838         var cfg= {
3839             tag: 'li',
3840             cls: 'dropdown-menu-item',
3841             cn: [ anc ]
3842         };
3843         if (this.parent().type == 'treeview') {
3844             cfg.cls = 'treeview-menu';
3845         }
3846         if (this.active) {
3847             cfg.cls += ' active';
3848         }
3849         
3850         
3851         
3852         anc.href = this.href || cfg.cn[0].href ;
3853         ctag.html = this.html || cfg.cn[0].html ;
3854         return cfg;
3855     },
3856     
3857     initEvents: function()
3858     {
3859         if (this.parent().type == 'treeview') {
3860             this.el.select('a').on('click', this.onClick, this);
3861         }
3862         
3863         if (this.menu) {
3864             this.menu.parentType = this.xtype;
3865             this.menu.triggerEl = this.el;
3866             this.menu = this.addxtype(Roo.apply({}, this.menu));
3867         }
3868         
3869     },
3870     onClick : function(e)
3871     {
3872         Roo.log('item on click ');
3873         
3874         if(this.preventDefault){
3875             e.preventDefault();
3876         }
3877         //this.parent().hideMenuItems();
3878         
3879         this.fireEvent('click', this, e);
3880     },
3881     getEl : function()
3882     {
3883         return this.el;
3884     } 
3885 });
3886
3887  
3888
3889  /*
3890  * - LGPL
3891  *
3892  * menu separator
3893  * 
3894  */
3895
3896
3897 /**
3898  * @class Roo.bootstrap.MenuSeparator
3899  * @extends Roo.bootstrap.Component
3900  * Bootstrap MenuSeparator class
3901  * 
3902  * @constructor
3903  * Create a new MenuItem
3904  * @param {Object} config The config object
3905  */
3906
3907
3908 Roo.bootstrap.MenuSeparator = function(config){
3909     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3910 };
3911
3912 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3913     
3914     getAutoCreate : function(){
3915         var cfg = {
3916             cls: 'divider',
3917             tag : 'li'
3918         };
3919         
3920         return cfg;
3921     }
3922    
3923 });
3924
3925  
3926
3927  
3928 /*
3929 * Licence: LGPL
3930 */
3931
3932 /**
3933  * @class Roo.bootstrap.Modal
3934  * @extends Roo.bootstrap.Component
3935  * Bootstrap Modal class
3936  * @cfg {String} title Title of dialog
3937  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3938  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3939  * @cfg {Boolean} specificTitle default false
3940  * @cfg {Array} buttons Array of buttons or standard button set..
3941  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3942  * @cfg {Boolean} animate default true
3943  * @cfg {Boolean} allow_close default true
3944  * @cfg {Boolean} fitwindow default false
3945  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3946  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3947  * @cfg {String} size (sm|lg|xl) default empty
3948  * @cfg {Number} max_width set the max width of modal
3949  * @cfg {Boolean} editableTitle can the title be edited
3950
3951  *
3952  *
3953  * @constructor
3954  * Create a new Modal Dialog
3955  * @param {Object} config The config object
3956  */
3957
3958 Roo.bootstrap.Modal = function(config){
3959     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3960     this.addEvents({
3961         // raw events
3962         /**
3963          * @event btnclick
3964          * The raw btnclick event for the button
3965          * @param {Roo.EventObject} e
3966          */
3967         "btnclick" : true,
3968         /**
3969          * @event resize
3970          * Fire when dialog resize
3971          * @param {Roo.bootstrap.Modal} this
3972          * @param {Roo.EventObject} e
3973          */
3974         "resize" : true,
3975         /**
3976          * @event titlechanged
3977          * Fire when the editable title has been changed
3978          * @param {Roo.bootstrap.Modal} this
3979          * @param {Roo.EventObject} value
3980          */
3981         "titlechanged" : true 
3982         
3983     });
3984     this.buttons = this.buttons || [];
3985
3986     if (this.tmpl) {
3987         this.tmpl = Roo.factory(this.tmpl);
3988     }
3989
3990 };
3991
3992 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3993
3994     title : 'test dialog',
3995
3996     buttons : false,
3997
3998     // set on load...
3999
4000     html: false,
4001
4002     tmp: false,
4003
4004     specificTitle: false,
4005
4006     buttonPosition: 'right',
4007
4008     allow_close : true,
4009
4010     animate : true,
4011
4012     fitwindow: false,
4013     
4014      // private
4015     dialogEl: false,
4016     bodyEl:  false,
4017     footerEl:  false,
4018     titleEl:  false,
4019     closeEl:  false,
4020
4021     size: '',
4022     
4023     max_width: 0,
4024     
4025     max_height: 0,
4026     
4027     fit_content: false,
4028     editableTitle  : false,
4029
4030     onRender : function(ct, position)
4031     {
4032         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4033
4034         if(!this.el){
4035             var cfg = Roo.apply({},  this.getAutoCreate());
4036             cfg.id = Roo.id();
4037             //if(!cfg.name){
4038             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4039             //}
4040             //if (!cfg.name.length) {
4041             //    delete cfg.name;
4042            // }
4043             if (this.cls) {
4044                 cfg.cls += ' ' + this.cls;
4045             }
4046             if (this.style) {
4047                 cfg.style = this.style;
4048             }
4049             this.el = Roo.get(document.body).createChild(cfg, position);
4050         }
4051         //var type = this.el.dom.type;
4052
4053
4054         if(this.tabIndex !== undefined){
4055             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4056         }
4057
4058         this.dialogEl = this.el.select('.modal-dialog',true).first();
4059         this.bodyEl = this.el.select('.modal-body',true).first();
4060         this.closeEl = this.el.select('.modal-header .close', true).first();
4061         this.headerEl = this.el.select('.modal-header',true).first();
4062         this.titleEl = this.el.select('.modal-title',true).first();
4063         this.footerEl = this.el.select('.modal-footer',true).first();
4064
4065         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4066         
4067         //this.el.addClass("x-dlg-modal");
4068
4069         if (this.buttons.length) {
4070             Roo.each(this.buttons, function(bb) {
4071                 var b = Roo.apply({}, bb);
4072                 b.xns = b.xns || Roo.bootstrap;
4073                 b.xtype = b.xtype || 'Button';
4074                 if (typeof(b.listeners) == 'undefined') {
4075                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4076                 }
4077
4078                 var btn = Roo.factory(b);
4079
4080                 btn.render(this.getButtonContainer());
4081
4082             },this);
4083         }
4084         // render the children.
4085         var nitems = [];
4086
4087         if(typeof(this.items) != 'undefined'){
4088             var items = this.items;
4089             delete this.items;
4090
4091             for(var i =0;i < items.length;i++) {
4092                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4093             }
4094         }
4095
4096         this.items = nitems;
4097
4098         // where are these used - they used to be body/close/footer
4099
4100
4101         this.initEvents();
4102         //this.el.addClass([this.fieldClass, this.cls]);
4103
4104     },
4105
4106     getAutoCreate : function()
4107     {
4108         // we will default to modal-body-overflow - might need to remove or make optional later.
4109         var bdy = {
4110                 cls : 'modal-body enable-modal-body-overflow ', 
4111                 html : this.html || ''
4112         };
4113
4114         var title = {
4115             tag: 'h4',
4116             cls : 'modal-title',
4117             html : this.title
4118         };
4119
4120         if(this.specificTitle){ // WTF is this?
4121             title = this.title;
4122         }
4123
4124         var header = [];
4125         if (this.allow_close && Roo.bootstrap.version == 3) {
4126             header.push({
4127                 tag: 'button',
4128                 cls : 'close',
4129                 html : '&times'
4130             });
4131         }
4132
4133         header.push(title);
4134
4135         if (this.editableTitle) {
4136             header.push({
4137                 cls: 'form-control roo-editable-title d-none',
4138                 tag: 'input',
4139                 type: 'text'
4140             });
4141         }
4142         
4143         if (this.allow_close && Roo.bootstrap.version == 4) {
4144             header.push({
4145                 tag: 'button',
4146                 cls : 'close',
4147                 html : '&times'
4148             });
4149         }
4150         
4151         var size = '';
4152
4153         if(this.size.length){
4154             size = 'modal-' + this.size;
4155         }
4156         
4157         var footer = Roo.bootstrap.version == 3 ?
4158             {
4159                 cls : 'modal-footer',
4160                 cn : [
4161                     {
4162                         tag: 'div',
4163                         cls: 'btn-' + this.buttonPosition
4164                     }
4165                 ]
4166
4167             } :
4168             {  // BS4 uses mr-auto on left buttons....
4169                 cls : 'modal-footer'
4170             };
4171
4172             
4173
4174         
4175         
4176         var modal = {
4177             cls: "modal",
4178              cn : [
4179                 {
4180                     cls: "modal-dialog " + size,
4181                     cn : [
4182                         {
4183                             cls : "modal-content",
4184                             cn : [
4185                                 {
4186                                     cls : 'modal-header',
4187                                     cn : header
4188                                 },
4189                                 bdy,
4190                                 footer
4191                             ]
4192
4193                         }
4194                     ]
4195
4196                 }
4197             ]
4198         };
4199
4200         if(this.animate){
4201             modal.cls += ' fade';
4202         }
4203
4204         return modal;
4205
4206     },
4207     getChildContainer : function() {
4208
4209          return this.bodyEl;
4210
4211     },
4212     getButtonContainer : function() {
4213         
4214          return Roo.bootstrap.version == 4 ?
4215             this.el.select('.modal-footer',true).first()
4216             : this.el.select('.modal-footer div',true).first();
4217
4218     },
4219     initEvents : function()
4220     {
4221         if (this.allow_close) {
4222             this.closeEl.on('click', this.hide, this);
4223         }
4224         Roo.EventManager.onWindowResize(this.resize, this, true);
4225         if (this.editableTitle) {
4226             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4227             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4228             this.headerEditEl.on('keyup', function(e) {
4229                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4230                         this.toggleHeaderInput(false)
4231                     }
4232                 }, this);
4233             this.headerEditEl.on('blur', function(e) {
4234                 this.toggleHeaderInput(false)
4235             },this);
4236         }
4237
4238     },
4239   
4240
4241     resize : function()
4242     {
4243         this.maskEl.setSize(
4244             Roo.lib.Dom.getViewWidth(true),
4245             Roo.lib.Dom.getViewHeight(true)
4246         );
4247         
4248         if (this.fitwindow) {
4249             
4250            
4251             this.setSize(
4252                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4253                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4254             );
4255             return;
4256         }
4257         
4258         if(this.max_width !== 0) {
4259             
4260             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4261             
4262             if(this.height) {
4263                 this.setSize(w, this.height);
4264                 return;
4265             }
4266             
4267             if(this.max_height) {
4268                 this.setSize(w,Math.min(
4269                     this.max_height,
4270                     Roo.lib.Dom.getViewportHeight(true) - 60
4271                 ));
4272                 
4273                 return;
4274             }
4275             
4276             if(!this.fit_content) {
4277                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4278                 return;
4279             }
4280             
4281             this.setSize(w, Math.min(
4282                 60 +
4283                 this.headerEl.getHeight() + 
4284                 this.footerEl.getHeight() + 
4285                 this.getChildHeight(this.bodyEl.dom.childNodes),
4286                 Roo.lib.Dom.getViewportHeight(true) - 60)
4287             );
4288         }
4289         
4290     },
4291
4292     setSize : function(w,h)
4293     {
4294         if (!w && !h) {
4295             return;
4296         }
4297         
4298         this.resizeTo(w,h);
4299     },
4300
4301     show : function() {
4302
4303         if (!this.rendered) {
4304             this.render();
4305         }
4306         this.toggleHeaderInput(false);
4307         //this.el.setStyle('display', 'block');
4308         this.el.removeClass('hideing');
4309         this.el.dom.style.display='block';
4310         
4311         Roo.get(document.body).addClass('modal-open');
4312  
4313         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4314             
4315             (function(){
4316                 this.el.addClass('show');
4317                 this.el.addClass('in');
4318             }).defer(50, this);
4319         }else{
4320             this.el.addClass('show');
4321             this.el.addClass('in');
4322         }
4323
4324         // not sure how we can show data in here..
4325         //if (this.tmpl) {
4326         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4327         //}
4328
4329         Roo.get(document.body).addClass("x-body-masked");
4330         
4331         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4332         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4333         this.maskEl.dom.style.display = 'block';
4334         this.maskEl.addClass('show');
4335         
4336         
4337         this.resize();
4338         
4339         this.fireEvent('show', this);
4340
4341         // set zindex here - otherwise it appears to be ignored...
4342         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4343
4344         (function () {
4345             this.items.forEach( function(e) {
4346                 e.layout ? e.layout() : false;
4347
4348             });
4349         }).defer(100,this);
4350
4351     },
4352     hide : function()
4353     {
4354         if(this.fireEvent("beforehide", this) !== false){
4355             
4356             this.maskEl.removeClass('show');
4357             
4358             this.maskEl.dom.style.display = '';
4359             Roo.get(document.body).removeClass("x-body-masked");
4360             this.el.removeClass('in');
4361             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4362
4363             if(this.animate){ // why
4364                 this.el.addClass('hideing');
4365                 this.el.removeClass('show');
4366                 (function(){
4367                     if (!this.el.hasClass('hideing')) {
4368                         return; // it's been shown again...
4369                     }
4370                     
4371                     this.el.dom.style.display='';
4372
4373                     Roo.get(document.body).removeClass('modal-open');
4374                     this.el.removeClass('hideing');
4375                 }).defer(150,this);
4376                 
4377             }else{
4378                 this.el.removeClass('show');
4379                 this.el.dom.style.display='';
4380                 Roo.get(document.body).removeClass('modal-open');
4381
4382             }
4383             this.fireEvent('hide', this);
4384         }
4385     },
4386     isVisible : function()
4387     {
4388         
4389         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4390         
4391     },
4392
4393     addButton : function(str, cb)
4394     {
4395
4396
4397         var b = Roo.apply({}, { html : str } );
4398         b.xns = b.xns || Roo.bootstrap;
4399         b.xtype = b.xtype || 'Button';
4400         if (typeof(b.listeners) == 'undefined') {
4401             b.listeners = { click : cb.createDelegate(this)  };
4402         }
4403
4404         var btn = Roo.factory(b);
4405
4406         btn.render(this.getButtonContainer());
4407
4408         return btn;
4409
4410     },
4411
4412     setDefaultButton : function(btn)
4413     {
4414         //this.el.select('.modal-footer').()
4415     },
4416
4417     resizeTo: function(w,h)
4418     {
4419         this.dialogEl.setWidth(w);
4420         
4421         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4422
4423         this.bodyEl.setHeight(h - diff);
4424         
4425         this.fireEvent('resize', this);
4426     },
4427     
4428     setContentSize  : function(w, h)
4429     {
4430
4431     },
4432     onButtonClick: function(btn,e)
4433     {
4434         //Roo.log([a,b,c]);
4435         this.fireEvent('btnclick', btn.name, e);
4436     },
4437      /**
4438      * Set the title of the Dialog
4439      * @param {String} str new Title
4440      */
4441     setTitle: function(str) {
4442         this.titleEl.dom.innerHTML = str;
4443         this.title = str;
4444     },
4445     /**
4446      * Set the body of the Dialog
4447      * @param {String} str new Title
4448      */
4449     setBody: function(str) {
4450         this.bodyEl.dom.innerHTML = str;
4451     },
4452     /**
4453      * Set the body of the Dialog using the template
4454      * @param {Obj} data - apply this data to the template and replace the body contents.
4455      */
4456     applyBody: function(obj)
4457     {
4458         if (!this.tmpl) {
4459             Roo.log("Error - using apply Body without a template");
4460             //code
4461         }
4462         this.tmpl.overwrite(this.bodyEl, obj);
4463     },
4464     
4465     getChildHeight : function(child_nodes)
4466     {
4467         if(
4468             !child_nodes ||
4469             child_nodes.length == 0
4470         ) {
4471             return 0;
4472         }
4473         
4474         var child_height = 0;
4475         
4476         for(var i = 0; i < child_nodes.length; i++) {
4477             
4478             /*
4479             * for modal with tabs...
4480             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4481                 
4482                 var layout_childs = child_nodes[i].childNodes;
4483                 
4484                 for(var j = 0; j < layout_childs.length; j++) {
4485                     
4486                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4487                         
4488                         var layout_body_childs = layout_childs[j].childNodes;
4489                         
4490                         for(var k = 0; k < layout_body_childs.length; k++) {
4491                             
4492                             if(layout_body_childs[k].classList.contains('navbar')) {
4493                                 child_height += layout_body_childs[k].offsetHeight;
4494                                 continue;
4495                             }
4496                             
4497                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4498                                 
4499                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4500                                 
4501                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4502                                     
4503                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4504                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4505                                         continue;
4506                                     }
4507                                     
4508                                 }
4509                                 
4510                             }
4511                             
4512                         }
4513                     }
4514                 }
4515                 continue;
4516             }
4517             */
4518             
4519             child_height += child_nodes[i].offsetHeight;
4520             // Roo.log(child_nodes[i].offsetHeight);
4521         }
4522         
4523         return child_height;
4524     },
4525     toggleHeaderInput : function(is_edit)
4526     {
4527         if (!this.editableTitle) {
4528             return; // not editable.
4529         }
4530         if (is_edit && this.is_header_editing) {
4531             return; // already editing..
4532         }
4533         if (is_edit) {
4534     
4535             this.headerEditEl.dom.value = this.title;
4536             this.headerEditEl.removeClass('d-none');
4537             this.headerEditEl.dom.focus();
4538             this.titleEl.addClass('d-none');
4539             
4540             this.is_header_editing = true;
4541             return
4542         }
4543         // flip back to not editing.
4544         this.title = this.headerEditEl.dom.value;
4545         this.headerEditEl.addClass('d-none');
4546         this.titleEl.removeClass('d-none');
4547         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4548         this.is_header_editing = false;
4549         this.fireEvent('titlechanged', this, this.title);
4550     
4551             
4552         
4553     }
4554
4555 });
4556
4557
4558 Roo.apply(Roo.bootstrap.Modal,  {
4559     /**
4560          * Button config that displays a single OK button
4561          * @type Object
4562          */
4563         OK :  [{
4564             name : 'ok',
4565             weight : 'primary',
4566             html : 'OK'
4567         }],
4568         /**
4569          * Button config that displays Yes and No buttons
4570          * @type Object
4571          */
4572         YESNO : [
4573             {
4574                 name  : 'no',
4575                 html : 'No'
4576             },
4577             {
4578                 name  :'yes',
4579                 weight : 'primary',
4580                 html : 'Yes'
4581             }
4582         ],
4583
4584         /**
4585          * Button config that displays OK and Cancel buttons
4586          * @type Object
4587          */
4588         OKCANCEL : [
4589             {
4590                name : 'cancel',
4591                 html : 'Cancel'
4592             },
4593             {
4594                 name : 'ok',
4595                 weight : 'primary',
4596                 html : 'OK'
4597             }
4598         ],
4599         /**
4600          * Button config that displays Yes, No and Cancel buttons
4601          * @type Object
4602          */
4603         YESNOCANCEL : [
4604             {
4605                 name : 'yes',
4606                 weight : 'primary',
4607                 html : 'Yes'
4608             },
4609             {
4610                 name : 'no',
4611                 html : 'No'
4612             },
4613             {
4614                 name : 'cancel',
4615                 html : 'Cancel'
4616             }
4617         ],
4618         
4619         zIndex : 10001
4620 });
4621
4622 /*
4623  * - LGPL
4624  *
4625  * messagebox - can be used as a replace
4626  * 
4627  */
4628 /**
4629  * @class Roo.MessageBox
4630  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4631  * Example usage:
4632  *<pre><code>
4633 // Basic alert:
4634 Roo.Msg.alert('Status', 'Changes saved successfully.');
4635
4636 // Prompt for user data:
4637 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4638     if (btn == 'ok'){
4639         // process text value...
4640     }
4641 });
4642
4643 // Show a dialog using config options:
4644 Roo.Msg.show({
4645    title:'Save Changes?',
4646    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4647    buttons: Roo.Msg.YESNOCANCEL,
4648    fn: processResult,
4649    animEl: 'elId'
4650 });
4651 </code></pre>
4652  * @singleton
4653  */
4654 Roo.bootstrap.MessageBox = function(){
4655     var dlg, opt, mask, waitTimer;
4656     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4657     var buttons, activeTextEl, bwidth;
4658
4659     
4660     // private
4661     var handleButton = function(button){
4662         dlg.hide();
4663         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4664     };
4665
4666     // private
4667     var handleHide = function(){
4668         if(opt && opt.cls){
4669             dlg.el.removeClass(opt.cls);
4670         }
4671         //if(waitTimer){
4672         //    Roo.TaskMgr.stop(waitTimer);
4673         //    waitTimer = null;
4674         //}
4675     };
4676
4677     // private
4678     var updateButtons = function(b){
4679         var width = 0;
4680         if(!b){
4681             buttons["ok"].hide();
4682             buttons["cancel"].hide();
4683             buttons["yes"].hide();
4684             buttons["no"].hide();
4685             dlg.footerEl.hide();
4686             
4687             return width;
4688         }
4689         dlg.footerEl.show();
4690         for(var k in buttons){
4691             if(typeof buttons[k] != "function"){
4692                 if(b[k]){
4693                     buttons[k].show();
4694                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4695                     width += buttons[k].el.getWidth()+15;
4696                 }else{
4697                     buttons[k].hide();
4698                 }
4699             }
4700         }
4701         return width;
4702     };
4703
4704     // private
4705     var handleEsc = function(d, k, e){
4706         if(opt && opt.closable !== false){
4707             dlg.hide();
4708         }
4709         if(e){
4710             e.stopEvent();
4711         }
4712     };
4713
4714     return {
4715         /**
4716          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4717          * @return {Roo.BasicDialog} The BasicDialog element
4718          */
4719         getDialog : function(){
4720            if(!dlg){
4721                 dlg = new Roo.bootstrap.Modal( {
4722                     //draggable: true,
4723                     //resizable:false,
4724                     //constraintoviewport:false,
4725                     //fixedcenter:true,
4726                     //collapsible : false,
4727                     //shim:true,
4728                     //modal: true,
4729                 //    width: 'auto',
4730                   //  height:100,
4731                     //buttonAlign:"center",
4732                     closeClick : function(){
4733                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4734                             handleButton("no");
4735                         }else{
4736                             handleButton("cancel");
4737                         }
4738                     }
4739                 });
4740                 dlg.render();
4741                 dlg.on("hide", handleHide);
4742                 mask = dlg.mask;
4743                 //dlg.addKeyListener(27, handleEsc);
4744                 buttons = {};
4745                 this.buttons = buttons;
4746                 var bt = this.buttonText;
4747                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4748                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4749                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4750                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4751                 //Roo.log(buttons);
4752                 bodyEl = dlg.bodyEl.createChild({
4753
4754                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4755                         '<textarea class="roo-mb-textarea"></textarea>' +
4756                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4757                 });
4758                 msgEl = bodyEl.dom.firstChild;
4759                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4760                 textboxEl.enableDisplayMode();
4761                 textboxEl.addKeyListener([10,13], function(){
4762                     if(dlg.isVisible() && opt && opt.buttons){
4763                         if(opt.buttons.ok){
4764                             handleButton("ok");
4765                         }else if(opt.buttons.yes){
4766                             handleButton("yes");
4767                         }
4768                     }
4769                 });
4770                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4771                 textareaEl.enableDisplayMode();
4772                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4773                 progressEl.enableDisplayMode();
4774                 
4775                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4776                 var pf = progressEl.dom.firstChild;
4777                 if (pf) {
4778                     pp = Roo.get(pf.firstChild);
4779                     pp.setHeight(pf.offsetHeight);
4780                 }
4781                 
4782             }
4783             return dlg;
4784         },
4785
4786         /**
4787          * Updates the message box body text
4788          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4789          * the XHTML-compliant non-breaking space character '&amp;#160;')
4790          * @return {Roo.MessageBox} This message box
4791          */
4792         updateText : function(text)
4793         {
4794             if(!dlg.isVisible() && !opt.width){
4795                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4796                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4797             }
4798             msgEl.innerHTML = text || '&#160;';
4799       
4800             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4801             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4802             var w = Math.max(
4803                     Math.min(opt.width || cw , this.maxWidth), 
4804                     Math.max(opt.minWidth || this.minWidth, bwidth)
4805             );
4806             if(opt.prompt){
4807                 activeTextEl.setWidth(w);
4808             }
4809             if(dlg.isVisible()){
4810                 dlg.fixedcenter = false;
4811             }
4812             // to big, make it scroll. = But as usual stupid IE does not support
4813             // !important..
4814             
4815             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4816                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4817                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4818             } else {
4819                 bodyEl.dom.style.height = '';
4820                 bodyEl.dom.style.overflowY = '';
4821             }
4822             if (cw > w) {
4823                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4824             } else {
4825                 bodyEl.dom.style.overflowX = '';
4826             }
4827             
4828             dlg.setContentSize(w, bodyEl.getHeight());
4829             if(dlg.isVisible()){
4830                 dlg.fixedcenter = true;
4831             }
4832             return this;
4833         },
4834
4835         /**
4836          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4837          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4838          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4839          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4840          * @return {Roo.MessageBox} This message box
4841          */
4842         updateProgress : function(value, text){
4843             if(text){
4844                 this.updateText(text);
4845             }
4846             
4847             if (pp) { // weird bug on my firefox - for some reason this is not defined
4848                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4849                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4850             }
4851             return this;
4852         },        
4853
4854         /**
4855          * Returns true if the message box is currently displayed
4856          * @return {Boolean} True if the message box is visible, else false
4857          */
4858         isVisible : function(){
4859             return dlg && dlg.isVisible();  
4860         },
4861
4862         /**
4863          * Hides the message box if it is displayed
4864          */
4865         hide : function(){
4866             if(this.isVisible()){
4867                 dlg.hide();
4868             }  
4869         },
4870
4871         /**
4872          * Displays a new message box, or reinitializes an existing message box, based on the config options
4873          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4874          * The following config object properties are supported:
4875          * <pre>
4876 Property    Type             Description
4877 ----------  ---------------  ------------------------------------------------------------------------------------
4878 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4879                                    closes (defaults to undefined)
4880 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4881                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4882 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4883                                    progress and wait dialogs will ignore this property and always hide the
4884                                    close button as they can only be closed programmatically.
4885 cls               String           A custom CSS class to apply to the message box element
4886 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4887                                    displayed (defaults to 75)
4888 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4889                                    function will be btn (the name of the button that was clicked, if applicable,
4890                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4891                                    Progress and wait dialogs will ignore this option since they do not respond to
4892                                    user actions and can only be closed programmatically, so any required function
4893                                    should be called by the same code after it closes the dialog.
4894 icon              String           A CSS class that provides a background image to be used as an icon for
4895                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4896 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4897 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4898 modal             Boolean          False to allow user interaction with the page while the message box is
4899                                    displayed (defaults to true)
4900 msg               String           A string that will replace the existing message box body text (defaults
4901                                    to the XHTML-compliant non-breaking space character '&#160;')
4902 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4903 progress          Boolean          True to display a progress bar (defaults to false)
4904 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4905 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4906 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4907 title             String           The title text
4908 value             String           The string value to set into the active textbox element if displayed
4909 wait              Boolean          True to display a progress bar (defaults to false)
4910 width             Number           The width of the dialog in pixels
4911 </pre>
4912          *
4913          * Example usage:
4914          * <pre><code>
4915 Roo.Msg.show({
4916    title: 'Address',
4917    msg: 'Please enter your address:',
4918    width: 300,
4919    buttons: Roo.MessageBox.OKCANCEL,
4920    multiline: true,
4921    fn: saveAddress,
4922    animEl: 'addAddressBtn'
4923 });
4924 </code></pre>
4925          * @param {Object} config Configuration options
4926          * @return {Roo.MessageBox} This message box
4927          */
4928         show : function(options)
4929         {
4930             
4931             // this causes nightmares if you show one dialog after another
4932             // especially on callbacks..
4933              
4934             if(this.isVisible()){
4935                 
4936                 this.hide();
4937                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4938                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4939                 Roo.log("New Dialog Message:" +  options.msg )
4940                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4941                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4942                 
4943             }
4944             var d = this.getDialog();
4945             opt = options;
4946             d.setTitle(opt.title || "&#160;");
4947             d.closeEl.setDisplayed(opt.closable !== false);
4948             activeTextEl = textboxEl;
4949             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4950             if(opt.prompt){
4951                 if(opt.multiline){
4952                     textboxEl.hide();
4953                     textareaEl.show();
4954                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4955                         opt.multiline : this.defaultTextHeight);
4956                     activeTextEl = textareaEl;
4957                 }else{
4958                     textboxEl.show();
4959                     textareaEl.hide();
4960                 }
4961             }else{
4962                 textboxEl.hide();
4963                 textareaEl.hide();
4964             }
4965             progressEl.setDisplayed(opt.progress === true);
4966             if (opt.progress) {
4967                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4968             }
4969             this.updateProgress(0);
4970             activeTextEl.dom.value = opt.value || "";
4971             if(opt.prompt){
4972                 dlg.setDefaultButton(activeTextEl);
4973             }else{
4974                 var bs = opt.buttons;
4975                 var db = null;
4976                 if(bs && bs.ok){
4977                     db = buttons["ok"];
4978                 }else if(bs && bs.yes){
4979                     db = buttons["yes"];
4980                 }
4981                 dlg.setDefaultButton(db);
4982             }
4983             bwidth = updateButtons(opt.buttons);
4984             this.updateText(opt.msg);
4985             if(opt.cls){
4986                 d.el.addClass(opt.cls);
4987             }
4988             d.proxyDrag = opt.proxyDrag === true;
4989             d.modal = opt.modal !== false;
4990             d.mask = opt.modal !== false ? mask : false;
4991             if(!d.isVisible()){
4992                 // force it to the end of the z-index stack so it gets a cursor in FF
4993                 document.body.appendChild(dlg.el.dom);
4994                 d.animateTarget = null;
4995                 d.show(options.animEl);
4996             }
4997             return this;
4998         },
4999
5000         /**
5001          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5002          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5003          * and closing the message box when the process is complete.
5004          * @param {String} title The title bar text
5005          * @param {String} msg The message box body text
5006          * @return {Roo.MessageBox} This message box
5007          */
5008         progress : function(title, msg){
5009             this.show({
5010                 title : title,
5011                 msg : msg,
5012                 buttons: false,
5013                 progress:true,
5014                 closable:false,
5015                 minWidth: this.minProgressWidth,
5016                 modal : true
5017             });
5018             return this;
5019         },
5020
5021         /**
5022          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5023          * If a callback function is passed it will be called after the user clicks the button, and the
5024          * id of the button that was clicked will be passed as the only parameter to the callback
5025          * (could also be the top-right close button).
5026          * @param {String} title The title bar text
5027          * @param {String} msg The message box body text
5028          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5029          * @param {Object} scope (optional) The scope of the callback function
5030          * @return {Roo.MessageBox} This message box
5031          */
5032         alert : function(title, msg, fn, scope)
5033         {
5034             this.show({
5035                 title : title,
5036                 msg : msg,
5037                 buttons: this.OK,
5038                 fn: fn,
5039                 closable : false,
5040                 scope : scope,
5041                 modal : true
5042             });
5043             return this;
5044         },
5045
5046         /**
5047          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5048          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5049          * You are responsible for closing the message box when the process is complete.
5050          * @param {String} msg The message box body text
5051          * @param {String} title (optional) The title bar text
5052          * @return {Roo.MessageBox} This message box
5053          */
5054         wait : function(msg, title){
5055             this.show({
5056                 title : title,
5057                 msg : msg,
5058                 buttons: false,
5059                 closable:false,
5060                 progress:true,
5061                 modal:true,
5062                 width:300,
5063                 wait:true
5064             });
5065             waitTimer = Roo.TaskMgr.start({
5066                 run: function(i){
5067                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5068                 },
5069                 interval: 1000
5070             });
5071             return this;
5072         },
5073
5074         /**
5075          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5076          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5077          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5078          * @param {String} title The title bar text
5079          * @param {String} msg The message box body text
5080          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5081          * @param {Object} scope (optional) The scope of the callback function
5082          * @return {Roo.MessageBox} This message box
5083          */
5084         confirm : function(title, msg, fn, scope){
5085             this.show({
5086                 title : title,
5087                 msg : msg,
5088                 buttons: this.YESNO,
5089                 fn: fn,
5090                 scope : scope,
5091                 modal : true
5092             });
5093             return this;
5094         },
5095
5096         /**
5097          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5098          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5099          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5100          * (could also be the top-right close button) and the text that was entered will be passed as the two
5101          * parameters to the callback.
5102          * @param {String} title The title bar text
5103          * @param {String} msg The message box body text
5104          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5105          * @param {Object} scope (optional) The scope of the callback function
5106          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5107          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5108          * @return {Roo.MessageBox} This message box
5109          */
5110         prompt : function(title, msg, fn, scope, multiline){
5111             this.show({
5112                 title : title,
5113                 msg : msg,
5114                 buttons: this.OKCANCEL,
5115                 fn: fn,
5116                 minWidth:250,
5117                 scope : scope,
5118                 prompt:true,
5119                 multiline: multiline,
5120                 modal : true
5121             });
5122             return this;
5123         },
5124
5125         /**
5126          * Button config that displays a single OK button
5127          * @type Object
5128          */
5129         OK : {ok:true},
5130         /**
5131          * Button config that displays Yes and No buttons
5132          * @type Object
5133          */
5134         YESNO : {yes:true, no:true},
5135         /**
5136          * Button config that displays OK and Cancel buttons
5137          * @type Object
5138          */
5139         OKCANCEL : {ok:true, cancel:true},
5140         /**
5141          * Button config that displays Yes, No and Cancel buttons
5142          * @type Object
5143          */
5144         YESNOCANCEL : {yes:true, no:true, cancel:true},
5145
5146         /**
5147          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5148          * @type Number
5149          */
5150         defaultTextHeight : 75,
5151         /**
5152          * The maximum width in pixels of the message box (defaults to 600)
5153          * @type Number
5154          */
5155         maxWidth : 600,
5156         /**
5157          * The minimum width in pixels of the message box (defaults to 100)
5158          * @type Number
5159          */
5160         minWidth : 100,
5161         /**
5162          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5163          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5164          * @type Number
5165          */
5166         minProgressWidth : 250,
5167         /**
5168          * An object containing the default button text strings that can be overriden for localized language support.
5169          * Supported properties are: ok, cancel, yes and no.
5170          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5171          * @type Object
5172          */
5173         buttonText : {
5174             ok : "OK",
5175             cancel : "Cancel",
5176             yes : "Yes",
5177             no : "No"
5178         }
5179     };
5180 }();
5181
5182 /**
5183  * Shorthand for {@link Roo.MessageBox}
5184  */
5185 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5186 Roo.Msg = Roo.Msg || Roo.MessageBox;
5187 /*
5188  * - LGPL
5189  *
5190  * navbar
5191  * 
5192  */
5193
5194 /**
5195  * @class Roo.bootstrap.Navbar
5196  * @extends Roo.bootstrap.Component
5197  * Bootstrap Navbar class
5198
5199  * @constructor
5200  * Create a new Navbar
5201  * @param {Object} config The config object
5202  */
5203
5204
5205 Roo.bootstrap.Navbar = function(config){
5206     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5207     this.addEvents({
5208         // raw events
5209         /**
5210          * @event beforetoggle
5211          * Fire before toggle the menu
5212          * @param {Roo.EventObject} e
5213          */
5214         "beforetoggle" : true
5215     });
5216 };
5217
5218 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5219     
5220     
5221    
5222     // private
5223     navItems : false,
5224     loadMask : false,
5225     
5226     
5227     getAutoCreate : function(){
5228         
5229         
5230         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5231         
5232     },
5233     
5234     initEvents :function ()
5235     {
5236         //Roo.log(this.el.select('.navbar-toggle',true));
5237         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5238         
5239         var mark = {
5240             tag: "div",
5241             cls:"x-dlg-mask"
5242         };
5243         
5244         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5245         
5246         var size = this.el.getSize();
5247         this.maskEl.setSize(size.width, size.height);
5248         this.maskEl.enableDisplayMode("block");
5249         this.maskEl.hide();
5250         
5251         if(this.loadMask){
5252             this.maskEl.show();
5253         }
5254     },
5255     
5256     
5257     getChildContainer : function()
5258     {
5259         if (this.el && this.el.select('.collapse').getCount()) {
5260             return this.el.select('.collapse',true).first();
5261         }
5262         
5263         return this.el;
5264     },
5265     
5266     mask : function()
5267     {
5268         this.maskEl.show();
5269     },
5270     
5271     unmask : function()
5272     {
5273         this.maskEl.hide();
5274     },
5275     onToggle : function()
5276     {
5277         
5278         if(this.fireEvent('beforetoggle', this) === false){
5279             return;
5280         }
5281         var ce = this.el.select('.navbar-collapse',true).first();
5282       
5283         if (!ce.hasClass('show')) {
5284            this.expand();
5285         } else {
5286             this.collapse();
5287         }
5288         
5289         
5290     
5291     },
5292     /**
5293      * Expand the navbar pulldown 
5294      */
5295     expand : function ()
5296     {
5297        
5298         var ce = this.el.select('.navbar-collapse',true).first();
5299         if (ce.hasClass('collapsing')) {
5300             return;
5301         }
5302         ce.dom.style.height = '';
5303                // show it...
5304         ce.addClass('in'); // old...
5305         ce.removeClass('collapse');
5306         ce.addClass('show');
5307         var h = ce.getHeight();
5308         Roo.log(h);
5309         ce.removeClass('show');
5310         // at this point we should be able to see it..
5311         ce.addClass('collapsing');
5312         
5313         ce.setHeight(0); // resize it ...
5314         ce.on('transitionend', function() {
5315             //Roo.log('done transition');
5316             ce.removeClass('collapsing');
5317             ce.addClass('show');
5318             ce.removeClass('collapse');
5319
5320             ce.dom.style.height = '';
5321         }, this, { single: true} );
5322         ce.setHeight(h);
5323         ce.dom.scrollTop = 0;
5324     },
5325     /**
5326      * Collapse the navbar pulldown 
5327      */
5328     collapse : function()
5329     {
5330          var ce = this.el.select('.navbar-collapse',true).first();
5331        
5332         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5333             // it's collapsed or collapsing..
5334             return;
5335         }
5336         ce.removeClass('in'); // old...
5337         ce.setHeight(ce.getHeight());
5338         ce.removeClass('show');
5339         ce.addClass('collapsing');
5340         
5341         ce.on('transitionend', function() {
5342             ce.dom.style.height = '';
5343             ce.removeClass('collapsing');
5344             ce.addClass('collapse');
5345         }, this, { single: true} );
5346         ce.setHeight(0);
5347     }
5348     
5349     
5350     
5351 });
5352
5353
5354
5355  
5356
5357  /*
5358  * - LGPL
5359  *
5360  * navbar
5361  * 
5362  */
5363
5364 /**
5365  * @class Roo.bootstrap.NavSimplebar
5366  * @extends Roo.bootstrap.Navbar
5367  * Bootstrap Sidebar class
5368  *
5369  * @cfg {Boolean} inverse is inverted color
5370  * 
5371  * @cfg {String} type (nav | pills | tabs)
5372  * @cfg {Boolean} arrangement stacked | justified
5373  * @cfg {String} align (left | right) alignment
5374  * 
5375  * @cfg {Boolean} main (true|false) main nav bar? default false
5376  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5377  * 
5378  * @cfg {String} tag (header|footer|nav|div) default is nav 
5379
5380  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5381  * 
5382  * 
5383  * @constructor
5384  * Create a new Sidebar
5385  * @param {Object} config The config object
5386  */
5387
5388
5389 Roo.bootstrap.NavSimplebar = function(config){
5390     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5391 };
5392
5393 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5394     
5395     inverse: false,
5396     
5397     type: false,
5398     arrangement: '',
5399     align : false,
5400     
5401     weight : 'light',
5402     
5403     main : false,
5404     
5405     
5406     tag : false,
5407     
5408     
5409     getAutoCreate : function(){
5410         
5411         
5412         var cfg = {
5413             tag : this.tag || 'div',
5414             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5415         };
5416         if (['light','white'].indexOf(this.weight) > -1) {
5417             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5418         }
5419         cfg.cls += ' bg-' + this.weight;
5420         
5421         if (this.inverse) {
5422             cfg.cls += ' navbar-inverse';
5423             
5424         }
5425         
5426         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5427         
5428         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5429             return cfg;
5430         }
5431         
5432         
5433     
5434         
5435         cfg.cn = [
5436             {
5437                 cls: 'nav nav-' + this.xtype,
5438                 tag : 'ul'
5439             }
5440         ];
5441         
5442          
5443         this.type = this.type || 'nav';
5444         if (['tabs','pills'].indexOf(this.type) != -1) {
5445             cfg.cn[0].cls += ' nav-' + this.type
5446         
5447         
5448         } else {
5449             if (this.type!=='nav') {
5450                 Roo.log('nav type must be nav/tabs/pills')
5451             }
5452             cfg.cn[0].cls += ' navbar-nav'
5453         }
5454         
5455         
5456         
5457         
5458         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5459             cfg.cn[0].cls += ' nav-' + this.arrangement;
5460         }
5461         
5462         
5463         if (this.align === 'right') {
5464             cfg.cn[0].cls += ' navbar-right';
5465         }
5466         
5467         
5468         
5469         
5470         return cfg;
5471     
5472         
5473     }
5474     
5475     
5476     
5477 });
5478
5479
5480
5481  
5482
5483  
5484        /*
5485  * - LGPL
5486  *
5487  * navbar
5488  * navbar-fixed-top
5489  * navbar-expand-md  fixed-top 
5490  */
5491
5492 /**
5493  * @class Roo.bootstrap.NavHeaderbar
5494  * @extends Roo.bootstrap.NavSimplebar
5495  * Bootstrap Sidebar class
5496  *
5497  * @cfg {String} brand what is brand
5498  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5499  * @cfg {String} brand_href href of the brand
5500  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5501  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5502  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5503  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5504  * 
5505  * @constructor
5506  * Create a new Sidebar
5507  * @param {Object} config The config object
5508  */
5509
5510
5511 Roo.bootstrap.NavHeaderbar = function(config){
5512     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5513       
5514 };
5515
5516 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5517     
5518     position: '',
5519     brand: '',
5520     brand_href: false,
5521     srButton : true,
5522     autohide : false,
5523     desktopCenter : false,
5524    
5525     
5526     getAutoCreate : function(){
5527         
5528         var   cfg = {
5529             tag: this.nav || 'nav',
5530             cls: 'navbar navbar-expand-md',
5531             role: 'navigation',
5532             cn: []
5533         };
5534         
5535         var cn = cfg.cn;
5536         if (this.desktopCenter) {
5537             cn.push({cls : 'container', cn : []});
5538             cn = cn[0].cn;
5539         }
5540         
5541         if(this.srButton){
5542             var btn = {
5543                 tag: 'button',
5544                 type: 'button',
5545                 cls: 'navbar-toggle navbar-toggler',
5546                 'data-toggle': 'collapse',
5547                 cn: [
5548                     {
5549                         tag: 'span',
5550                         cls: 'sr-only',
5551                         html: 'Toggle navigation'
5552                     },
5553                     {
5554                         tag: 'span',
5555                         cls: 'icon-bar navbar-toggler-icon'
5556                     },
5557                     {
5558                         tag: 'span',
5559                         cls: 'icon-bar'
5560                     },
5561                     {
5562                         tag: 'span',
5563                         cls: 'icon-bar'
5564                     }
5565                 ]
5566             };
5567             
5568             cn.push( Roo.bootstrap.version == 4 ? btn : {
5569                 tag: 'div',
5570                 cls: 'navbar-header',
5571                 cn: [
5572                     btn
5573                 ]
5574             });
5575         }
5576         
5577         cn.push({
5578             tag: 'div',
5579             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5580             cn : []
5581         });
5582         
5583         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5584         
5585         if (['light','white'].indexOf(this.weight) > -1) {
5586             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5587         }
5588         cfg.cls += ' bg-' + this.weight;
5589         
5590         
5591         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5592             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5593             
5594             // tag can override this..
5595             
5596             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5597         }
5598         
5599         if (this.brand !== '') {
5600             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5601             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5602                 tag: 'a',
5603                 href: this.brand_href ? this.brand_href : '#',
5604                 cls: 'navbar-brand',
5605                 cn: [
5606                 this.brand
5607                 ]
5608             });
5609         }
5610         
5611         if(this.main){
5612             cfg.cls += ' main-nav';
5613         }
5614         
5615         
5616         return cfg;
5617
5618         
5619     },
5620     getHeaderChildContainer : function()
5621     {
5622         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5623             return this.el.select('.navbar-header',true).first();
5624         }
5625         
5626         return this.getChildContainer();
5627     },
5628     
5629     getChildContainer : function()
5630     {
5631          
5632         return this.el.select('.roo-navbar-collapse',true).first();
5633          
5634         
5635     },
5636     
5637     initEvents : function()
5638     {
5639         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5640         
5641         if (this.autohide) {
5642             
5643             var prevScroll = 0;
5644             var ft = this.el;
5645             
5646             Roo.get(document).on('scroll',function(e) {
5647                 var ns = Roo.get(document).getScroll().top;
5648                 var os = prevScroll;
5649                 prevScroll = ns;
5650                 
5651                 if(ns > os){
5652                     ft.removeClass('slideDown');
5653                     ft.addClass('slideUp');
5654                     return;
5655                 }
5656                 ft.removeClass('slideUp');
5657                 ft.addClass('slideDown');
5658                  
5659               
5660           },this);
5661         }
5662     }    
5663     
5664 });
5665
5666
5667
5668  
5669
5670  /*
5671  * - LGPL
5672  *
5673  * navbar
5674  * 
5675  */
5676
5677 /**
5678  * @class Roo.bootstrap.NavSidebar
5679  * @extends Roo.bootstrap.Navbar
5680  * Bootstrap Sidebar class
5681  * 
5682  * @constructor
5683  * Create a new Sidebar
5684  * @param {Object} config The config object
5685  */
5686
5687
5688 Roo.bootstrap.NavSidebar = function(config){
5689     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5690 };
5691
5692 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5693     
5694     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5695     
5696     getAutoCreate : function(){
5697         
5698         
5699         return  {
5700             tag: 'div',
5701             cls: 'sidebar sidebar-nav'
5702         };
5703     
5704         
5705     }
5706     
5707     
5708     
5709 });
5710
5711
5712
5713  
5714
5715  /*
5716  * - LGPL
5717  *
5718  * nav group
5719  * 
5720  */
5721
5722 /**
5723  * @class Roo.bootstrap.NavGroup
5724  * @extends Roo.bootstrap.Component
5725  * Bootstrap NavGroup class
5726  * @cfg {String} align (left|right)
5727  * @cfg {Boolean} inverse
5728  * @cfg {String} type (nav|pills|tab) default nav
5729  * @cfg {String} navId - reference Id for navbar.
5730
5731  * 
5732  * @constructor
5733  * Create a new nav group
5734  * @param {Object} config The config object
5735  */
5736
5737 Roo.bootstrap.NavGroup = function(config){
5738     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5739     this.navItems = [];
5740    
5741     Roo.bootstrap.NavGroup.register(this);
5742      this.addEvents({
5743         /**
5744              * @event changed
5745              * Fires when the active item changes
5746              * @param {Roo.bootstrap.NavGroup} this
5747              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5748              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5749          */
5750         'changed': true
5751      });
5752     
5753 };
5754
5755 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5756     
5757     align: '',
5758     inverse: false,
5759     form: false,
5760     type: 'nav',
5761     navId : '',
5762     // private
5763     
5764     navItems : false, 
5765     
5766     getAutoCreate : function()
5767     {
5768         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5769         
5770         cfg = {
5771             tag : 'ul',
5772             cls: 'nav' 
5773         };
5774         if (Roo.bootstrap.version == 4) {
5775             if (['tabs','pills'].indexOf(this.type) != -1) {
5776                 cfg.cls += ' nav-' + this.type; 
5777             } else {
5778                 // trying to remove so header bar can right align top?
5779                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5780                     // do not use on header bar... 
5781                     cfg.cls += ' navbar-nav';
5782                 }
5783             }
5784             
5785         } else {
5786             if (['tabs','pills'].indexOf(this.type) != -1) {
5787                 cfg.cls += ' nav-' + this.type
5788             } else {
5789                 if (this.type !== 'nav') {
5790                     Roo.log('nav type must be nav/tabs/pills')
5791                 }
5792                 cfg.cls += ' navbar-nav'
5793             }
5794         }
5795         
5796         if (this.parent() && this.parent().sidebar) {
5797             cfg = {
5798                 tag: 'ul',
5799                 cls: 'dashboard-menu sidebar-menu'
5800             };
5801             
5802             return cfg;
5803         }
5804         
5805         if (this.form === true) {
5806             cfg = {
5807                 tag: 'form',
5808                 cls: 'navbar-form form-inline'
5809             };
5810             //nav navbar-right ml-md-auto
5811             if (this.align === 'right') {
5812                 cfg.cls += ' navbar-right ml-md-auto';
5813             } else {
5814                 cfg.cls += ' navbar-left';
5815             }
5816         }
5817         
5818         if (this.align === 'right') {
5819             cfg.cls += ' navbar-right ml-md-auto';
5820         } else {
5821             cfg.cls += ' mr-auto';
5822         }
5823         
5824         if (this.inverse) {
5825             cfg.cls += ' navbar-inverse';
5826             
5827         }
5828         
5829         
5830         return cfg;
5831     },
5832     /**
5833     * sets the active Navigation item
5834     * @param {Roo.bootstrap.NavItem} the new current navitem
5835     */
5836     setActiveItem : function(item)
5837     {
5838         var prev = false;
5839         Roo.each(this.navItems, function(v){
5840             if (v == item) {
5841                 return ;
5842             }
5843             if (v.isActive()) {
5844                 v.setActive(false, true);
5845                 prev = v;
5846                 
5847             }
5848             
5849         });
5850
5851         item.setActive(true, true);
5852         this.fireEvent('changed', this, item, prev);
5853         
5854         
5855     },
5856     /**
5857     * gets the active Navigation item
5858     * @return {Roo.bootstrap.NavItem} the current navitem
5859     */
5860     getActive : function()
5861     {
5862         
5863         var prev = false;
5864         Roo.each(this.navItems, function(v){
5865             
5866             if (v.isActive()) {
5867                 prev = v;
5868                 
5869             }
5870             
5871         });
5872         return prev;
5873     },
5874     
5875     indexOfNav : function()
5876     {
5877         
5878         var prev = false;
5879         Roo.each(this.navItems, function(v,i){
5880             
5881             if (v.isActive()) {
5882                 prev = i;
5883                 
5884             }
5885             
5886         });
5887         return prev;
5888     },
5889     /**
5890     * adds a Navigation item
5891     * @param {Roo.bootstrap.NavItem} the navitem to add
5892     */
5893     addItem : function(cfg)
5894     {
5895         if (this.form && Roo.bootstrap.version == 4) {
5896             cfg.tag = 'div';
5897         }
5898         var cn = new Roo.bootstrap.NavItem(cfg);
5899         this.register(cn);
5900         cn.parentId = this.id;
5901         cn.onRender(this.el, null);
5902         return cn;
5903     },
5904     /**
5905     * register a Navigation item
5906     * @param {Roo.bootstrap.NavItem} the navitem to add
5907     */
5908     register : function(item)
5909     {
5910         this.navItems.push( item);
5911         item.navId = this.navId;
5912     
5913     },
5914     
5915     /**
5916     * clear all the Navigation item
5917     */
5918    
5919     clearAll : function()
5920     {
5921         this.navItems = [];
5922         this.el.dom.innerHTML = '';
5923     },
5924     
5925     getNavItem: function(tabId)
5926     {
5927         var ret = false;
5928         Roo.each(this.navItems, function(e) {
5929             if (e.tabId == tabId) {
5930                ret =  e;
5931                return false;
5932             }
5933             return true;
5934             
5935         });
5936         return ret;
5937     },
5938     
5939     setActiveNext : function()
5940     {
5941         var i = this.indexOfNav(this.getActive());
5942         if (i > this.navItems.length) {
5943             return;
5944         }
5945         this.setActiveItem(this.navItems[i+1]);
5946     },
5947     setActivePrev : function()
5948     {
5949         var i = this.indexOfNav(this.getActive());
5950         if (i  < 1) {
5951             return;
5952         }
5953         this.setActiveItem(this.navItems[i-1]);
5954     },
5955     clearWasActive : function(except) {
5956         Roo.each(this.navItems, function(e) {
5957             if (e.tabId != except.tabId && e.was_active) {
5958                e.was_active = false;
5959                return false;
5960             }
5961             return true;
5962             
5963         });
5964     },
5965     getWasActive : function ()
5966     {
5967         var r = false;
5968         Roo.each(this.navItems, function(e) {
5969             if (e.was_active) {
5970                r = e;
5971                return false;
5972             }
5973             return true;
5974             
5975         });
5976         return r;
5977     }
5978     
5979     
5980 });
5981
5982  
5983 Roo.apply(Roo.bootstrap.NavGroup, {
5984     
5985     groups: {},
5986      /**
5987     * register a Navigation Group
5988     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5989     */
5990     register : function(navgrp)
5991     {
5992         this.groups[navgrp.navId] = navgrp;
5993         
5994     },
5995     /**
5996     * fetch a Navigation Group based on the navigation ID
5997     * @param {string} the navgroup to add
5998     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5999     */
6000     get: function(navId) {
6001         if (typeof(this.groups[navId]) == 'undefined') {
6002             return false;
6003             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6004         }
6005         return this.groups[navId] ;
6006     }
6007     
6008     
6009     
6010 });
6011
6012  /*
6013  * - LGPL
6014  *
6015  * row
6016  * 
6017  */
6018
6019 /**
6020  * @class Roo.bootstrap.NavItem
6021  * @extends Roo.bootstrap.Component
6022  * Bootstrap Navbar.NavItem class
6023  * @cfg {String} href  link to
6024  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6025
6026  * @cfg {String} html content of button
6027  * @cfg {String} badge text inside badge
6028  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6029  * @cfg {String} glyphicon DEPRICATED - use fa
6030  * @cfg {String} icon DEPRICATED - use fa
6031  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6032  * @cfg {Boolean} active Is item active
6033  * @cfg {Boolean} disabled Is item disabled
6034  
6035  * @cfg {Boolean} preventDefault (true | false) default false
6036  * @cfg {String} tabId the tab that this item activates.
6037  * @cfg {String} tagtype (a|span) render as a href or span?
6038  * @cfg {Boolean} animateRef (true|false) link to element default false  
6039   
6040  * @constructor
6041  * Create a new Navbar Item
6042  * @param {Object} config The config object
6043  */
6044 Roo.bootstrap.NavItem = function(config){
6045     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6046     this.addEvents({
6047         // raw events
6048         /**
6049          * @event click
6050          * The raw click event for the entire grid.
6051          * @param {Roo.EventObject} e
6052          */
6053         "click" : true,
6054          /**
6055             * @event changed
6056             * Fires when the active item active state changes
6057             * @param {Roo.bootstrap.NavItem} this
6058             * @param {boolean} state the new state
6059              
6060          */
6061         'changed': true,
6062         /**
6063             * @event scrollto
6064             * Fires when scroll to element
6065             * @param {Roo.bootstrap.NavItem} this
6066             * @param {Object} options
6067             * @param {Roo.EventObject} e
6068              
6069          */
6070         'scrollto': true
6071     });
6072    
6073 };
6074
6075 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6076     
6077     href: false,
6078     html: '',
6079     badge: '',
6080     icon: false,
6081     fa : false,
6082     glyphicon: false,
6083     active: false,
6084     preventDefault : false,
6085     tabId : false,
6086     tagtype : 'a',
6087     tag: 'li',
6088     disabled : false,
6089     animateRef : false,
6090     was_active : false,
6091     button_weight : '',
6092     button_outline : false,
6093     
6094     navLink: false,
6095     
6096     getAutoCreate : function(){
6097          
6098         var cfg = {
6099             tag: this.tag,
6100             cls: 'nav-item'
6101         };
6102         
6103         if (this.active) {
6104             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6105         }
6106         if (this.disabled) {
6107             cfg.cls += ' disabled';
6108         }
6109         
6110         // BS4 only?
6111         if (this.button_weight.length) {
6112             cfg.tag = this.href ? 'a' : 'button';
6113             cfg.html = this.html || '';
6114             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6115             if (this.href) {
6116                 cfg.href = this.href;
6117             }
6118             if (this.fa) {
6119                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6120             }
6121             
6122             // menu .. should add dropdown-menu class - so no need for carat..
6123             
6124             if (this.badge !== '') {
6125                  
6126                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6127             }
6128             return cfg;
6129         }
6130         
6131         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6132             cfg.cn = [
6133                 {
6134                     tag: this.tagtype,
6135                     href : this.href || "#",
6136                     html: this.html || ''
6137                 }
6138             ];
6139             if (this.tagtype == 'a') {
6140                 cfg.cn[0].cls = 'nav-link';
6141             }
6142             if (this.icon) {
6143                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6144             }
6145             if (this.fa) {
6146                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6147             }
6148             if(this.glyphicon) {
6149                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6150             }
6151             
6152             if (this.menu) {
6153                 
6154                 cfg.cn[0].html += " <span class='caret'></span>";
6155              
6156             }
6157             
6158             if (this.badge !== '') {
6159                  
6160                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6161             }
6162         }
6163         
6164         
6165         
6166         return cfg;
6167     },
6168     onRender : function(ct, position)
6169     {
6170        // Roo.log("Call onRender: " + this.xtype);
6171         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6172             this.tag = 'div';
6173         }
6174         
6175         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6176         this.navLink = this.el.select('.nav-link',true).first();
6177         return ret;
6178     },
6179       
6180     
6181     initEvents: function() 
6182     {
6183         if (typeof (this.menu) != 'undefined') {
6184             this.menu.parentType = this.xtype;
6185             this.menu.triggerEl = this.el;
6186             this.menu = this.addxtype(Roo.apply({}, this.menu));
6187         }
6188         
6189         this.el.select('a',true).on('click', this.onClick, this);
6190         
6191         if(this.tagtype == 'span'){
6192             this.el.select('span',true).on('click', this.onClick, this);
6193         }
6194        
6195         // at this point parent should be available..
6196         this.parent().register(this);
6197     },
6198     
6199     onClick : function(e)
6200     {
6201         if (e.getTarget('.dropdown-menu-item')) {
6202             // did you click on a menu itemm.... - then don't trigger onclick..
6203             return;
6204         }
6205         
6206         if(
6207                 this.preventDefault || 
6208                 this.href == '#' 
6209         ){
6210             Roo.log("NavItem - prevent Default?");
6211             e.preventDefault();
6212         }
6213         
6214         if (this.disabled) {
6215             return;
6216         }
6217         
6218         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6219         if (tg && tg.transition) {
6220             Roo.log("waiting for the transitionend");
6221             return;
6222         }
6223         
6224         
6225         
6226         //Roo.log("fire event clicked");
6227         if(this.fireEvent('click', this, e) === false){
6228             return;
6229         };
6230         
6231         if(this.tagtype == 'span'){
6232             return;
6233         }
6234         
6235         //Roo.log(this.href);
6236         var ael = this.el.select('a',true).first();
6237         //Roo.log(ael);
6238         
6239         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6240             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6241             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6242                 return; // ignore... - it's a 'hash' to another page.
6243             }
6244             Roo.log("NavItem - prevent Default?");
6245             e.preventDefault();
6246             this.scrollToElement(e);
6247         }
6248         
6249         
6250         var p =  this.parent();
6251    
6252         if (['tabs','pills'].indexOf(p.type)!==-1) {
6253             if (typeof(p.setActiveItem) !== 'undefined') {
6254                 p.setActiveItem(this);
6255             }
6256         }
6257         
6258         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6259         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6260             // remove the collapsed menu expand...
6261             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6262         }
6263     },
6264     
6265     isActive: function () {
6266         return this.active
6267     },
6268     setActive : function(state, fire, is_was_active)
6269     {
6270         if (this.active && !state && this.navId) {
6271             this.was_active = true;
6272             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6273             if (nv) {
6274                 nv.clearWasActive(this);
6275             }
6276             
6277         }
6278         this.active = state;
6279         
6280         if (!state ) {
6281             this.el.removeClass('active');
6282             this.navLink ? this.navLink.removeClass('active') : false;
6283         } else if (!this.el.hasClass('active')) {
6284             
6285             this.el.addClass('active');
6286             if (Roo.bootstrap.version == 4 && this.navLink ) {
6287                 this.navLink.addClass('active');
6288             }
6289             
6290         }
6291         if (fire) {
6292             this.fireEvent('changed', this, state);
6293         }
6294         
6295         // show a panel if it's registered and related..
6296         
6297         if (!this.navId || !this.tabId || !state || is_was_active) {
6298             return;
6299         }
6300         
6301         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6302         if (!tg) {
6303             return;
6304         }
6305         var pan = tg.getPanelByName(this.tabId);
6306         if (!pan) {
6307             return;
6308         }
6309         // if we can not flip to new panel - go back to old nav highlight..
6310         if (false == tg.showPanel(pan)) {
6311             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6312             if (nv) {
6313                 var onav = nv.getWasActive();
6314                 if (onav) {
6315                     onav.setActive(true, false, true);
6316                 }
6317             }
6318             
6319         }
6320         
6321         
6322         
6323     },
6324      // this should not be here...
6325     setDisabled : function(state)
6326     {
6327         this.disabled = state;
6328         if (!state ) {
6329             this.el.removeClass('disabled');
6330         } else if (!this.el.hasClass('disabled')) {
6331             this.el.addClass('disabled');
6332         }
6333         
6334     },
6335     
6336     /**
6337      * Fetch the element to display the tooltip on.
6338      * @return {Roo.Element} defaults to this.el
6339      */
6340     tooltipEl : function()
6341     {
6342         return this.el.select('' + this.tagtype + '', true).first();
6343     },
6344     
6345     scrollToElement : function(e)
6346     {
6347         var c = document.body;
6348         
6349         /*
6350          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6351          */
6352         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6353             c = document.documentElement;
6354         }
6355         
6356         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6357         
6358         if(!target){
6359             return;
6360         }
6361
6362         var o = target.calcOffsetsTo(c);
6363         
6364         var options = {
6365             target : target,
6366             value : o[1]
6367         };
6368         
6369         this.fireEvent('scrollto', this, options, e);
6370         
6371         Roo.get(c).scrollTo('top', options.value, true);
6372         
6373         return;
6374     }
6375 });
6376  
6377
6378  /*
6379  * - LGPL
6380  *
6381  * sidebar item
6382  *
6383  *  li
6384  *    <span> icon </span>
6385  *    <span> text </span>
6386  *    <span>badge </span>
6387  */
6388
6389 /**
6390  * @class Roo.bootstrap.NavSidebarItem
6391  * @extends Roo.bootstrap.NavItem
6392  * Bootstrap Navbar.NavSidebarItem class
6393  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6394  * {Boolean} open is the menu open
6395  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6396  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6397  * {String} buttonSize (sm|md|lg)the extra classes for the button
6398  * {Boolean} showArrow show arrow next to the text (default true)
6399  * @constructor
6400  * Create a new Navbar Button
6401  * @param {Object} config The config object
6402  */
6403 Roo.bootstrap.NavSidebarItem = function(config){
6404     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6405     this.addEvents({
6406         // raw events
6407         /**
6408          * @event click
6409          * The raw click event for the entire grid.
6410          * @param {Roo.EventObject} e
6411          */
6412         "click" : true,
6413          /**
6414             * @event changed
6415             * Fires when the active item active state changes
6416             * @param {Roo.bootstrap.NavSidebarItem} this
6417             * @param {boolean} state the new state
6418              
6419          */
6420         'changed': true
6421     });
6422    
6423 };
6424
6425 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6426     
6427     badgeWeight : 'default',
6428     
6429     open: false,
6430     
6431     buttonView : false,
6432     
6433     buttonWeight : 'default',
6434     
6435     buttonSize : 'md',
6436     
6437     showArrow : true,
6438     
6439     getAutoCreate : function(){
6440         
6441         
6442         var a = {
6443                 tag: 'a',
6444                 href : this.href || '#',
6445                 cls: '',
6446                 html : '',
6447                 cn : []
6448         };
6449         
6450         if(this.buttonView){
6451             a = {
6452                 tag: 'button',
6453                 href : this.href || '#',
6454                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6455                 html : this.html,
6456                 cn : []
6457             };
6458         }
6459         
6460         var cfg = {
6461             tag: 'li',
6462             cls: '',
6463             cn: [ a ]
6464         };
6465         
6466         if (this.active) {
6467             cfg.cls += ' active';
6468         }
6469         
6470         if (this.disabled) {
6471             cfg.cls += ' disabled';
6472         }
6473         if (this.open) {
6474             cfg.cls += ' open x-open';
6475         }
6476         // left icon..
6477         if (this.glyphicon || this.icon) {
6478             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6479             a.cn.push({ tag : 'i', cls : c }) ;
6480         }
6481         
6482         if(!this.buttonView){
6483             var span = {
6484                 tag: 'span',
6485                 html : this.html || ''
6486             };
6487
6488             a.cn.push(span);
6489             
6490         }
6491         
6492         if (this.badge !== '') {
6493             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6494         }
6495         
6496         if (this.menu) {
6497             
6498             if(this.showArrow){
6499                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6500             }
6501             
6502             a.cls += ' dropdown-toggle treeview' ;
6503         }
6504         
6505         return cfg;
6506     },
6507     
6508     initEvents : function()
6509     { 
6510         if (typeof (this.menu) != 'undefined') {
6511             this.menu.parentType = this.xtype;
6512             this.menu.triggerEl = this.el;
6513             this.menu = this.addxtype(Roo.apply({}, this.menu));
6514         }
6515         
6516         this.el.on('click', this.onClick, this);
6517         
6518         if(this.badge !== ''){
6519             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6520         }
6521         
6522     },
6523     
6524     onClick : function(e)
6525     {
6526         if(this.disabled){
6527             e.preventDefault();
6528             return;
6529         }
6530         
6531         if(this.preventDefault){
6532             e.preventDefault();
6533         }
6534         
6535         this.fireEvent('click', this, e);
6536     },
6537     
6538     disable : function()
6539     {
6540         this.setDisabled(true);
6541     },
6542     
6543     enable : function()
6544     {
6545         this.setDisabled(false);
6546     },
6547     
6548     setDisabled : function(state)
6549     {
6550         if(this.disabled == state){
6551             return;
6552         }
6553         
6554         this.disabled = state;
6555         
6556         if (state) {
6557             this.el.addClass('disabled');
6558             return;
6559         }
6560         
6561         this.el.removeClass('disabled');
6562         
6563         return;
6564     },
6565     
6566     setActive : function(state)
6567     {
6568         if(this.active == state){
6569             return;
6570         }
6571         
6572         this.active = state;
6573         
6574         if (state) {
6575             this.el.addClass('active');
6576             return;
6577         }
6578         
6579         this.el.removeClass('active');
6580         
6581         return;
6582     },
6583     
6584     isActive: function () 
6585     {
6586         return this.active;
6587     },
6588     
6589     setBadge : function(str)
6590     {
6591         if(!this.badgeEl){
6592             return;
6593         }
6594         
6595         this.badgeEl.dom.innerHTML = str;
6596     }
6597     
6598    
6599      
6600  
6601 });
6602  
6603
6604  /*
6605  * - LGPL
6606  *
6607  * row
6608  * 
6609  */
6610
6611 /**
6612  * @class Roo.bootstrap.Row
6613  * @extends Roo.bootstrap.Component
6614  * Bootstrap Row class (contains columns...)
6615  * 
6616  * @constructor
6617  * Create a new Row
6618  * @param {Object} config The config object
6619  */
6620
6621 Roo.bootstrap.Row = function(config){
6622     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6623 };
6624
6625 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6626     
6627     getAutoCreate : function(){
6628        return {
6629             cls: 'row clearfix'
6630        };
6631     }
6632     
6633     
6634 });
6635
6636  
6637
6638  /*
6639  * - LGPL
6640  *
6641  * pagination
6642  * 
6643  */
6644
6645 /**
6646  * @class Roo.bootstrap.Pagination
6647  * @extends Roo.bootstrap.Component
6648  * Bootstrap Pagination class
6649  * @cfg {String} size xs | sm | md | lg
6650  * @cfg {Boolean} inverse false | true
6651  * 
6652  * @constructor
6653  * Create a new Pagination
6654  * @param {Object} config The config object
6655  */
6656
6657 Roo.bootstrap.Pagination = function(config){
6658     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6659 };
6660
6661 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6662     
6663     cls: false,
6664     size: false,
6665     inverse: false,
6666     
6667     getAutoCreate : function(){
6668         var cfg = {
6669             tag: 'ul',
6670                 cls: 'pagination'
6671         };
6672         if (this.inverse) {
6673             cfg.cls += ' inverse';
6674         }
6675         if (this.html) {
6676             cfg.html=this.html;
6677         }
6678         if (this.cls) {
6679             cfg.cls += " " + this.cls;
6680         }
6681         return cfg;
6682     }
6683    
6684 });
6685
6686  
6687
6688  /*
6689  * - LGPL
6690  *
6691  * Pagination item
6692  * 
6693  */
6694
6695
6696 /**
6697  * @class Roo.bootstrap.PaginationItem
6698  * @extends Roo.bootstrap.Component
6699  * Bootstrap PaginationItem class
6700  * @cfg {String} html text
6701  * @cfg {String} href the link
6702  * @cfg {Boolean} preventDefault (true | false) default true
6703  * @cfg {Boolean} active (true | false) default false
6704  * @cfg {Boolean} disabled default false
6705  * 
6706  * 
6707  * @constructor
6708  * Create a new PaginationItem
6709  * @param {Object} config The config object
6710  */
6711
6712
6713 Roo.bootstrap.PaginationItem = function(config){
6714     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6715     this.addEvents({
6716         // raw events
6717         /**
6718          * @event click
6719          * The raw click event for the entire grid.
6720          * @param {Roo.EventObject} e
6721          */
6722         "click" : true
6723     });
6724 };
6725
6726 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6727     
6728     href : false,
6729     html : false,
6730     preventDefault: true,
6731     active : false,
6732     cls : false,
6733     disabled: false,
6734     
6735     getAutoCreate : function(){
6736         var cfg= {
6737             tag: 'li',
6738             cn: [
6739                 {
6740                     tag : 'a',
6741                     href : this.href ? this.href : '#',
6742                     html : this.html ? this.html : ''
6743                 }
6744             ]
6745         };
6746         
6747         if(this.cls){
6748             cfg.cls = this.cls;
6749         }
6750         
6751         if(this.disabled){
6752             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6753         }
6754         
6755         if(this.active){
6756             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6757         }
6758         
6759         return cfg;
6760     },
6761     
6762     initEvents: function() {
6763         
6764         this.el.on('click', this.onClick, this);
6765         
6766     },
6767     onClick : function(e)
6768     {
6769         Roo.log('PaginationItem on click ');
6770         if(this.preventDefault){
6771             e.preventDefault();
6772         }
6773         
6774         if(this.disabled){
6775             return;
6776         }
6777         
6778         this.fireEvent('click', this, e);
6779     }
6780    
6781 });
6782
6783  
6784
6785  /*
6786  * - LGPL
6787  *
6788  * slider
6789  * 
6790  */
6791
6792
6793 /**
6794  * @class Roo.bootstrap.Slider
6795  * @extends Roo.bootstrap.Component
6796  * Bootstrap Slider class
6797  *    
6798  * @constructor
6799  * Create a new Slider
6800  * @param {Object} config The config object
6801  */
6802
6803 Roo.bootstrap.Slider = function(config){
6804     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6805 };
6806
6807 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6808     
6809     getAutoCreate : function(){
6810         
6811         var cfg = {
6812             tag: 'div',
6813             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6814             cn: [
6815                 {
6816                     tag: 'a',
6817                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6818                 }
6819             ]
6820         };
6821         
6822         return cfg;
6823     }
6824    
6825 });
6826
6827  /*
6828  * Based on:
6829  * Ext JS Library 1.1.1
6830  * Copyright(c) 2006-2007, Ext JS, LLC.
6831  *
6832  * Originally Released Under LGPL - original licence link has changed is not relivant.
6833  *
6834  * Fork - LGPL
6835  * <script type="text/javascript">
6836  */
6837  
6838
6839 /**
6840  * @class Roo.grid.ColumnModel
6841  * @extends Roo.util.Observable
6842  * This is the default implementation of a ColumnModel used by the Grid. It defines
6843  * the columns in the grid.
6844  * <br>Usage:<br>
6845  <pre><code>
6846  var colModel = new Roo.grid.ColumnModel([
6847         {header: "Ticker", width: 60, sortable: true, locked: true},
6848         {header: "Company Name", width: 150, sortable: true},
6849         {header: "Market Cap.", width: 100, sortable: true},
6850         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6851         {header: "Employees", width: 100, sortable: true, resizable: false}
6852  ]);
6853  </code></pre>
6854  * <p>
6855  
6856  * The config options listed for this class are options which may appear in each
6857  * individual column definition.
6858  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6859  * @constructor
6860  * @param {Object} config An Array of column config objects. See this class's
6861  * config objects for details.
6862 */
6863 Roo.grid.ColumnModel = function(config){
6864         /**
6865      * The config passed into the constructor
6866      */
6867     this.config = config;
6868     this.lookup = {};
6869
6870     // if no id, create one
6871     // if the column does not have a dataIndex mapping,
6872     // map it to the order it is in the config
6873     for(var i = 0, len = config.length; i < len; i++){
6874         var c = config[i];
6875         if(typeof c.dataIndex == "undefined"){
6876             c.dataIndex = i;
6877         }
6878         if(typeof c.renderer == "string"){
6879             c.renderer = Roo.util.Format[c.renderer];
6880         }
6881         if(typeof c.id == "undefined"){
6882             c.id = Roo.id();
6883         }
6884         if(c.editor && c.editor.xtype){
6885             c.editor  = Roo.factory(c.editor, Roo.grid);
6886         }
6887         if(c.editor && c.editor.isFormField){
6888             c.editor = new Roo.grid.GridEditor(c.editor);
6889         }
6890         this.lookup[c.id] = c;
6891     }
6892
6893     /**
6894      * The width of columns which have no width specified (defaults to 100)
6895      * @type Number
6896      */
6897     this.defaultWidth = 100;
6898
6899     /**
6900      * Default sortable of columns which have no sortable specified (defaults to false)
6901      * @type Boolean
6902      */
6903     this.defaultSortable = false;
6904
6905     this.addEvents({
6906         /**
6907              * @event widthchange
6908              * Fires when the width of a column changes.
6909              * @param {ColumnModel} this
6910              * @param {Number} columnIndex The column index
6911              * @param {Number} newWidth The new width
6912              */
6913             "widthchange": true,
6914         /**
6915              * @event headerchange
6916              * Fires when the text of a header changes.
6917              * @param {ColumnModel} this
6918              * @param {Number} columnIndex The column index
6919              * @param {Number} newText The new header text
6920              */
6921             "headerchange": true,
6922         /**
6923              * @event hiddenchange
6924              * Fires when a column is hidden or "unhidden".
6925              * @param {ColumnModel} this
6926              * @param {Number} columnIndex The column index
6927              * @param {Boolean} hidden true if hidden, false otherwise
6928              */
6929             "hiddenchange": true,
6930             /**
6931          * @event columnmoved
6932          * Fires when a column is moved.
6933          * @param {ColumnModel} this
6934          * @param {Number} oldIndex
6935          * @param {Number} newIndex
6936          */
6937         "columnmoved" : true,
6938         /**
6939          * @event columlockchange
6940          * Fires when a column's locked state is changed
6941          * @param {ColumnModel} this
6942          * @param {Number} colIndex
6943          * @param {Boolean} locked true if locked
6944          */
6945         "columnlockchange" : true
6946     });
6947     Roo.grid.ColumnModel.superclass.constructor.call(this);
6948 };
6949 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6950     /**
6951      * @cfg {String} header The header text to display in the Grid view.
6952      */
6953     /**
6954      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6955      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6956      * specified, the column's index is used as an index into the Record's data Array.
6957      */
6958     /**
6959      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6960      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6961      */
6962     /**
6963      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6964      * Defaults to the value of the {@link #defaultSortable} property.
6965      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6966      */
6967     /**
6968      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6969      */
6970     /**
6971      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6972      */
6973     /**
6974      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6975      */
6976     /**
6977      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6978      */
6979     /**
6980      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6981      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6982      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6983      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6984      */
6985        /**
6986      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6987      */
6988     /**
6989      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6990      */
6991     /**
6992      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6993      */
6994     /**
6995      * @cfg {String} cursor (Optional)
6996      */
6997     /**
6998      * @cfg {String} tooltip (Optional)
6999      */
7000     /**
7001      * @cfg {Number} xs (Optional)
7002      */
7003     /**
7004      * @cfg {Number} sm (Optional)
7005      */
7006     /**
7007      * @cfg {Number} md (Optional)
7008      */
7009     /**
7010      * @cfg {Number} lg (Optional)
7011      */
7012     /**
7013      * Returns the id of the column at the specified index.
7014      * @param {Number} index The column index
7015      * @return {String} the id
7016      */
7017     getColumnId : function(index){
7018         return this.config[index].id;
7019     },
7020
7021     /**
7022      * Returns the column for a specified id.
7023      * @param {String} id The column id
7024      * @return {Object} the column
7025      */
7026     getColumnById : function(id){
7027         return this.lookup[id];
7028     },
7029
7030     
7031     /**
7032      * Returns the column for a specified dataIndex.
7033      * @param {String} dataIndex The column dataIndex
7034      * @return {Object|Boolean} the column or false if not found
7035      */
7036     getColumnByDataIndex: function(dataIndex){
7037         var index = this.findColumnIndex(dataIndex);
7038         return index > -1 ? this.config[index] : false;
7039     },
7040     
7041     /**
7042      * Returns the index for a specified column id.
7043      * @param {String} id The column id
7044      * @return {Number} the index, or -1 if not found
7045      */
7046     getIndexById : function(id){
7047         for(var i = 0, len = this.config.length; i < len; i++){
7048             if(this.config[i].id == id){
7049                 return i;
7050             }
7051         }
7052         return -1;
7053     },
7054     
7055     /**
7056      * Returns the index for a specified column dataIndex.
7057      * @param {String} dataIndex The column dataIndex
7058      * @return {Number} the index, or -1 if not found
7059      */
7060     
7061     findColumnIndex : function(dataIndex){
7062         for(var i = 0, len = this.config.length; i < len; i++){
7063             if(this.config[i].dataIndex == dataIndex){
7064                 return i;
7065             }
7066         }
7067         return -1;
7068     },
7069     
7070     
7071     moveColumn : function(oldIndex, newIndex){
7072         var c = this.config[oldIndex];
7073         this.config.splice(oldIndex, 1);
7074         this.config.splice(newIndex, 0, c);
7075         this.dataMap = null;
7076         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7077     },
7078
7079     isLocked : function(colIndex){
7080         return this.config[colIndex].locked === true;
7081     },
7082
7083     setLocked : function(colIndex, value, suppressEvent){
7084         if(this.isLocked(colIndex) == value){
7085             return;
7086         }
7087         this.config[colIndex].locked = value;
7088         if(!suppressEvent){
7089             this.fireEvent("columnlockchange", this, colIndex, value);
7090         }
7091     },
7092
7093     getTotalLockedWidth : function(){
7094         var totalWidth = 0;
7095         for(var i = 0; i < this.config.length; i++){
7096             if(this.isLocked(i) && !this.isHidden(i)){
7097                 this.totalWidth += this.getColumnWidth(i);
7098             }
7099         }
7100         return totalWidth;
7101     },
7102
7103     getLockedCount : function(){
7104         for(var i = 0, len = this.config.length; i < len; i++){
7105             if(!this.isLocked(i)){
7106                 return i;
7107             }
7108         }
7109         
7110         return this.config.length;
7111     },
7112
7113     /**
7114      * Returns the number of columns.
7115      * @return {Number}
7116      */
7117     getColumnCount : function(visibleOnly){
7118         if(visibleOnly === true){
7119             var c = 0;
7120             for(var i = 0, len = this.config.length; i < len; i++){
7121                 if(!this.isHidden(i)){
7122                     c++;
7123                 }
7124             }
7125             return c;
7126         }
7127         return this.config.length;
7128     },
7129
7130     /**
7131      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7132      * @param {Function} fn
7133      * @param {Object} scope (optional)
7134      * @return {Array} result
7135      */
7136     getColumnsBy : function(fn, scope){
7137         var r = [];
7138         for(var i = 0, len = this.config.length; i < len; i++){
7139             var c = this.config[i];
7140             if(fn.call(scope||this, c, i) === true){
7141                 r[r.length] = c;
7142             }
7143         }
7144         return r;
7145     },
7146
7147     /**
7148      * Returns true if the specified column is sortable.
7149      * @param {Number} col The column index
7150      * @return {Boolean}
7151      */
7152     isSortable : function(col){
7153         if(typeof this.config[col].sortable == "undefined"){
7154             return this.defaultSortable;
7155         }
7156         return this.config[col].sortable;
7157     },
7158
7159     /**
7160      * Returns the rendering (formatting) function defined for the column.
7161      * @param {Number} col The column index.
7162      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7163      */
7164     getRenderer : function(col){
7165         if(!this.config[col].renderer){
7166             return Roo.grid.ColumnModel.defaultRenderer;
7167         }
7168         return this.config[col].renderer;
7169     },
7170
7171     /**
7172      * Sets the rendering (formatting) function for a column.
7173      * @param {Number} col The column index
7174      * @param {Function} fn The function to use to process the cell's raw data
7175      * to return HTML markup for the grid view. The render function is called with
7176      * the following parameters:<ul>
7177      * <li>Data value.</li>
7178      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7179      * <li>css A CSS style string to apply to the table cell.</li>
7180      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7181      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7182      * <li>Row index</li>
7183      * <li>Column index</li>
7184      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7185      */
7186     setRenderer : function(col, fn){
7187         this.config[col].renderer = fn;
7188     },
7189
7190     /**
7191      * Returns the width for the specified column.
7192      * @param {Number} col The column index
7193      * @return {Number}
7194      */
7195     getColumnWidth : function(col){
7196         return this.config[col].width * 1 || this.defaultWidth;
7197     },
7198
7199     /**
7200      * Sets the width for a column.
7201      * @param {Number} col The column index
7202      * @param {Number} width The new width
7203      */
7204     setColumnWidth : function(col, width, suppressEvent){
7205         this.config[col].width = width;
7206         this.totalWidth = null;
7207         if(!suppressEvent){
7208              this.fireEvent("widthchange", this, col, width);
7209         }
7210     },
7211
7212     /**
7213      * Returns the total width of all columns.
7214      * @param {Boolean} includeHidden True to include hidden column widths
7215      * @return {Number}
7216      */
7217     getTotalWidth : function(includeHidden){
7218         if(!this.totalWidth){
7219             this.totalWidth = 0;
7220             for(var i = 0, len = this.config.length; i < len; i++){
7221                 if(includeHidden || !this.isHidden(i)){
7222                     this.totalWidth += this.getColumnWidth(i);
7223                 }
7224             }
7225         }
7226         return this.totalWidth;
7227     },
7228
7229     /**
7230      * Returns the header for the specified column.
7231      * @param {Number} col The column index
7232      * @return {String}
7233      */
7234     getColumnHeader : function(col){
7235         return this.config[col].header;
7236     },
7237
7238     /**
7239      * Sets the header for a column.
7240      * @param {Number} col The column index
7241      * @param {String} header The new header
7242      */
7243     setColumnHeader : function(col, header){
7244         this.config[col].header = header;
7245         this.fireEvent("headerchange", this, col, header);
7246     },
7247
7248     /**
7249      * Returns the tooltip for the specified column.
7250      * @param {Number} col The column index
7251      * @return {String}
7252      */
7253     getColumnTooltip : function(col){
7254             return this.config[col].tooltip;
7255     },
7256     /**
7257      * Sets the tooltip for a column.
7258      * @param {Number} col The column index
7259      * @param {String} tooltip The new tooltip
7260      */
7261     setColumnTooltip : function(col, tooltip){
7262             this.config[col].tooltip = tooltip;
7263     },
7264
7265     /**
7266      * Returns the dataIndex for the specified column.
7267      * @param {Number} col The column index
7268      * @return {Number}
7269      */
7270     getDataIndex : function(col){
7271         return this.config[col].dataIndex;
7272     },
7273
7274     /**
7275      * Sets the dataIndex for a column.
7276      * @param {Number} col The column index
7277      * @param {Number} dataIndex The new dataIndex
7278      */
7279     setDataIndex : function(col, dataIndex){
7280         this.config[col].dataIndex = dataIndex;
7281     },
7282
7283     
7284     
7285     /**
7286      * Returns true if the cell is editable.
7287      * @param {Number} colIndex The column index
7288      * @param {Number} rowIndex The row index - this is nto actually used..?
7289      * @return {Boolean}
7290      */
7291     isCellEditable : function(colIndex, rowIndex){
7292         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7293     },
7294
7295     /**
7296      * Returns the editor defined for the cell/column.
7297      * return false or null to disable editing.
7298      * @param {Number} colIndex The column index
7299      * @param {Number} rowIndex The row index
7300      * @return {Object}
7301      */
7302     getCellEditor : function(colIndex, rowIndex){
7303         return this.config[colIndex].editor;
7304     },
7305
7306     /**
7307      * Sets if a column is editable.
7308      * @param {Number} col The column index
7309      * @param {Boolean} editable True if the column is editable
7310      */
7311     setEditable : function(col, editable){
7312         this.config[col].editable = editable;
7313     },
7314
7315
7316     /**
7317      * Returns true if the column is hidden.
7318      * @param {Number} colIndex The column index
7319      * @return {Boolean}
7320      */
7321     isHidden : function(colIndex){
7322         return this.config[colIndex].hidden;
7323     },
7324
7325
7326     /**
7327      * Returns true if the column width cannot be changed
7328      */
7329     isFixed : function(colIndex){
7330         return this.config[colIndex].fixed;
7331     },
7332
7333     /**
7334      * Returns true if the column can be resized
7335      * @return {Boolean}
7336      */
7337     isResizable : function(colIndex){
7338         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7339     },
7340     /**
7341      * Sets if a column is hidden.
7342      * @param {Number} colIndex The column index
7343      * @param {Boolean} hidden True if the column is hidden
7344      */
7345     setHidden : function(colIndex, hidden){
7346         this.config[colIndex].hidden = hidden;
7347         this.totalWidth = null;
7348         this.fireEvent("hiddenchange", this, colIndex, hidden);
7349     },
7350
7351     /**
7352      * Sets the editor for a column.
7353      * @param {Number} col The column index
7354      * @param {Object} editor The editor object
7355      */
7356     setEditor : function(col, editor){
7357         this.config[col].editor = editor;
7358     }
7359 });
7360
7361 Roo.grid.ColumnModel.defaultRenderer = function(value)
7362 {
7363     if(typeof value == "object") {
7364         return value;
7365     }
7366         if(typeof value == "string" && value.length < 1){
7367             return "&#160;";
7368         }
7369     
7370         return String.format("{0}", value);
7371 };
7372
7373 // Alias for backwards compatibility
7374 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7375 /*
7376  * Based on:
7377  * Ext JS Library 1.1.1
7378  * Copyright(c) 2006-2007, Ext JS, LLC.
7379  *
7380  * Originally Released Under LGPL - original licence link has changed is not relivant.
7381  *
7382  * Fork - LGPL
7383  * <script type="text/javascript">
7384  */
7385  
7386 /**
7387  * @class Roo.LoadMask
7388  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7389  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7390  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7391  * element's UpdateManager load indicator and will be destroyed after the initial load.
7392  * @constructor
7393  * Create a new LoadMask
7394  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7395  * @param {Object} config The config object
7396  */
7397 Roo.LoadMask = function(el, config){
7398     this.el = Roo.get(el);
7399     Roo.apply(this, config);
7400     if(this.store){
7401         this.store.on('beforeload', this.onBeforeLoad, this);
7402         this.store.on('load', this.onLoad, this);
7403         this.store.on('loadexception', this.onLoadException, this);
7404         this.removeMask = false;
7405     }else{
7406         var um = this.el.getUpdateManager();
7407         um.showLoadIndicator = false; // disable the default indicator
7408         um.on('beforeupdate', this.onBeforeLoad, this);
7409         um.on('update', this.onLoad, this);
7410         um.on('failure', this.onLoad, this);
7411         this.removeMask = true;
7412     }
7413 };
7414
7415 Roo.LoadMask.prototype = {
7416     /**
7417      * @cfg {Boolean} removeMask
7418      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7419      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7420      */
7421     /**
7422      * @cfg {String} msg
7423      * The text to display in a centered loading message box (defaults to 'Loading...')
7424      */
7425     msg : 'Loading...',
7426     /**
7427      * @cfg {String} msgCls
7428      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7429      */
7430     msgCls : 'x-mask-loading',
7431
7432     /**
7433      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7434      * @type Boolean
7435      */
7436     disabled: false,
7437
7438     /**
7439      * Disables the mask to prevent it from being displayed
7440      */
7441     disable : function(){
7442        this.disabled = true;
7443     },
7444
7445     /**
7446      * Enables the mask so that it can be displayed
7447      */
7448     enable : function(){
7449         this.disabled = false;
7450     },
7451     
7452     onLoadException : function()
7453     {
7454         Roo.log(arguments);
7455         
7456         if (typeof(arguments[3]) != 'undefined') {
7457             Roo.MessageBox.alert("Error loading",arguments[3]);
7458         } 
7459         /*
7460         try {
7461             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7462                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7463             }   
7464         } catch(e) {
7465             
7466         }
7467         */
7468     
7469         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7470     },
7471     // private
7472     onLoad : function()
7473     {
7474         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7475     },
7476
7477     // private
7478     onBeforeLoad : function(){
7479         if(!this.disabled){
7480             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7481         }
7482     },
7483
7484     // private
7485     destroy : function(){
7486         if(this.store){
7487             this.store.un('beforeload', this.onBeforeLoad, this);
7488             this.store.un('load', this.onLoad, this);
7489             this.store.un('loadexception', this.onLoadException, this);
7490         }else{
7491             var um = this.el.getUpdateManager();
7492             um.un('beforeupdate', this.onBeforeLoad, this);
7493             um.un('update', this.onLoad, this);
7494             um.un('failure', this.onLoad, this);
7495         }
7496     }
7497 };/*
7498  * - LGPL
7499  *
7500  * table
7501  * 
7502  */
7503
7504 /**
7505  * @class Roo.bootstrap.Table
7506  * @extends Roo.bootstrap.Component
7507  * Bootstrap Table class
7508  * @cfg {String} cls table class
7509  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7510  * @cfg {String} bgcolor Specifies the background color for a table
7511  * @cfg {Number} border Specifies whether the table cells should have borders or not
7512  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7513  * @cfg {Number} cellspacing Specifies the space between cells
7514  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7515  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7516  * @cfg {String} sortable Specifies that the table should be sortable
7517  * @cfg {String} summary Specifies a summary of the content of a table
7518  * @cfg {Number} width Specifies the width of a table
7519  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7520  * 
7521  * @cfg {boolean} striped Should the rows be alternative striped
7522  * @cfg {boolean} bordered Add borders to the table
7523  * @cfg {boolean} hover Add hover highlighting
7524  * @cfg {boolean} condensed Format condensed
7525  * @cfg {boolean} responsive Format condensed
7526  * @cfg {Boolean} loadMask (true|false) default false
7527  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7528  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7529  * @cfg {Boolean} rowSelection (true|false) default false
7530  * @cfg {Boolean} cellSelection (true|false) default false
7531  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7532  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7533  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7534  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7535  
7536  * 
7537  * @constructor
7538  * Create a new Table
7539  * @param {Object} config The config object
7540  */
7541
7542 Roo.bootstrap.Table = function(config){
7543     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7544     
7545   
7546     
7547     // BC...
7548     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7549     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7550     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7551     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7552     
7553     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7554     if (this.sm) {
7555         this.sm.grid = this;
7556         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7557         this.sm = this.selModel;
7558         this.sm.xmodule = this.xmodule || false;
7559     }
7560     
7561     if (this.cm && typeof(this.cm.config) == 'undefined') {
7562         this.colModel = new Roo.grid.ColumnModel(this.cm);
7563         this.cm = this.colModel;
7564         this.cm.xmodule = this.xmodule || false;
7565     }
7566     if (this.store) {
7567         this.store= Roo.factory(this.store, Roo.data);
7568         this.ds = this.store;
7569         this.ds.xmodule = this.xmodule || false;
7570          
7571     }
7572     if (this.footer && this.store) {
7573         this.footer.dataSource = this.ds;
7574         this.footer = Roo.factory(this.footer);
7575     }
7576     
7577     /** @private */
7578     this.addEvents({
7579         /**
7580          * @event cellclick
7581          * Fires when a cell is clicked
7582          * @param {Roo.bootstrap.Table} this
7583          * @param {Roo.Element} el
7584          * @param {Number} rowIndex
7585          * @param {Number} columnIndex
7586          * @param {Roo.EventObject} e
7587          */
7588         "cellclick" : true,
7589         /**
7590          * @event celldblclick
7591          * Fires when a cell is double clicked
7592          * @param {Roo.bootstrap.Table} this
7593          * @param {Roo.Element} el
7594          * @param {Number} rowIndex
7595          * @param {Number} columnIndex
7596          * @param {Roo.EventObject} e
7597          */
7598         "celldblclick" : true,
7599         /**
7600          * @event rowclick
7601          * Fires when a row is clicked
7602          * @param {Roo.bootstrap.Table} this
7603          * @param {Roo.Element} el
7604          * @param {Number} rowIndex
7605          * @param {Roo.EventObject} e
7606          */
7607         "rowclick" : true,
7608         /**
7609          * @event rowdblclick
7610          * Fires when a row is double clicked
7611          * @param {Roo.bootstrap.Table} this
7612          * @param {Roo.Element} el
7613          * @param {Number} rowIndex
7614          * @param {Roo.EventObject} e
7615          */
7616         "rowdblclick" : true,
7617         /**
7618          * @event mouseover
7619          * Fires when a mouseover occur
7620          * @param {Roo.bootstrap.Table} this
7621          * @param {Roo.Element} el
7622          * @param {Number} rowIndex
7623          * @param {Number} columnIndex
7624          * @param {Roo.EventObject} e
7625          */
7626         "mouseover" : true,
7627         /**
7628          * @event mouseout
7629          * Fires when a mouseout occur
7630          * @param {Roo.bootstrap.Table} this
7631          * @param {Roo.Element} el
7632          * @param {Number} rowIndex
7633          * @param {Number} columnIndex
7634          * @param {Roo.EventObject} e
7635          */
7636         "mouseout" : true,
7637         /**
7638          * @event rowclass
7639          * Fires when a row is rendered, so you can change add a style to it.
7640          * @param {Roo.bootstrap.Table} this
7641          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7642          */
7643         'rowclass' : true,
7644           /**
7645          * @event rowsrendered
7646          * Fires when all the  rows have been rendered
7647          * @param {Roo.bootstrap.Table} this
7648          */
7649         'rowsrendered' : true,
7650         /**
7651          * @event contextmenu
7652          * The raw contextmenu event for the entire grid.
7653          * @param {Roo.EventObject} e
7654          */
7655         "contextmenu" : true,
7656         /**
7657          * @event rowcontextmenu
7658          * Fires when a row is right clicked
7659          * @param {Roo.bootstrap.Table} this
7660          * @param {Number} rowIndex
7661          * @param {Roo.EventObject} e
7662          */
7663         "rowcontextmenu" : true,
7664         /**
7665          * @event cellcontextmenu
7666          * Fires when a cell is right clicked
7667          * @param {Roo.bootstrap.Table} this
7668          * @param {Number} rowIndex
7669          * @param {Number} cellIndex
7670          * @param {Roo.EventObject} e
7671          */
7672          "cellcontextmenu" : true,
7673          /**
7674          * @event headercontextmenu
7675          * Fires when a header is right clicked
7676          * @param {Roo.bootstrap.Table} this
7677          * @param {Number} columnIndex
7678          * @param {Roo.EventObject} e
7679          */
7680         "headercontextmenu" : true
7681     });
7682 };
7683
7684 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7685     
7686     cls: false,
7687     align: false,
7688     bgcolor: false,
7689     border: false,
7690     cellpadding: false,
7691     cellspacing: false,
7692     frame: false,
7693     rules: false,
7694     sortable: false,
7695     summary: false,
7696     width: false,
7697     striped : false,
7698     scrollBody : false,
7699     bordered: false,
7700     hover:  false,
7701     condensed : false,
7702     responsive : false,
7703     sm : false,
7704     cm : false,
7705     store : false,
7706     loadMask : false,
7707     footerShow : true,
7708     headerShow : true,
7709   
7710     rowSelection : false,
7711     cellSelection : false,
7712     layout : false,
7713     
7714     // Roo.Element - the tbody
7715     mainBody: false,
7716     // Roo.Element - thead element
7717     mainHead: false,
7718     
7719     container: false, // used by gridpanel...
7720     
7721     lazyLoad : false,
7722     
7723     CSS : Roo.util.CSS,
7724     
7725     auto_hide_footer : false,
7726     
7727     getAutoCreate : function()
7728     {
7729         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7730         
7731         cfg = {
7732             tag: 'table',
7733             cls : 'table',
7734             cn : []
7735         };
7736         if (this.scrollBody) {
7737             cfg.cls += ' table-body-fixed';
7738         }    
7739         if (this.striped) {
7740             cfg.cls += ' table-striped';
7741         }
7742         
7743         if (this.hover) {
7744             cfg.cls += ' table-hover';
7745         }
7746         if (this.bordered) {
7747             cfg.cls += ' table-bordered';
7748         }
7749         if (this.condensed) {
7750             cfg.cls += ' table-condensed';
7751         }
7752         if (this.responsive) {
7753             cfg.cls += ' table-responsive';
7754         }
7755         
7756         if (this.cls) {
7757             cfg.cls+=  ' ' +this.cls;
7758         }
7759         
7760         // this lot should be simplifed...
7761         var _t = this;
7762         var cp = [
7763             'align',
7764             'bgcolor',
7765             'border',
7766             'cellpadding',
7767             'cellspacing',
7768             'frame',
7769             'rules',
7770             'sortable',
7771             'summary',
7772             'width'
7773         ].forEach(function(k) {
7774             if (_t[k]) {
7775                 cfg[k] = _t[k];
7776             }
7777         });
7778         
7779         
7780         if (this.layout) {
7781             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7782         }
7783         
7784         if(this.store || this.cm){
7785             if(this.headerShow){
7786                 cfg.cn.push(this.renderHeader());
7787             }
7788             
7789             cfg.cn.push(this.renderBody());
7790             
7791             if(this.footerShow){
7792                 cfg.cn.push(this.renderFooter());
7793             }
7794             // where does this come from?
7795             //cfg.cls+=  ' TableGrid';
7796         }
7797         
7798         return { cn : [ cfg ] };
7799     },
7800     
7801     initEvents : function()
7802     {   
7803         if(!this.store || !this.cm){
7804             return;
7805         }
7806         if (this.selModel) {
7807             this.selModel.initEvents();
7808         }
7809         
7810         
7811         //Roo.log('initEvents with ds!!!!');
7812         
7813         this.mainBody = this.el.select('tbody', true).first();
7814         this.mainHead = this.el.select('thead', true).first();
7815         this.mainFoot = this.el.select('tfoot', true).first();
7816         
7817         
7818         
7819         var _this = this;
7820         
7821         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7822             e.on('click', _this.sort, _this);
7823         });
7824         
7825         this.mainBody.on("click", this.onClick, this);
7826         this.mainBody.on("dblclick", this.onDblClick, this);
7827         
7828         // why is this done????? = it breaks dialogs??
7829         //this.parent().el.setStyle('position', 'relative');
7830         
7831         
7832         if (this.footer) {
7833             this.footer.parentId = this.id;
7834             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7835             
7836             if(this.lazyLoad){
7837                 this.el.select('tfoot tr td').first().addClass('hide');
7838             }
7839         } 
7840         
7841         if(this.loadMask) {
7842             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7843         }
7844         
7845         this.store.on('load', this.onLoad, this);
7846         this.store.on('beforeload', this.onBeforeLoad, this);
7847         this.store.on('update', this.onUpdate, this);
7848         this.store.on('add', this.onAdd, this);
7849         this.store.on("clear", this.clear, this);
7850         
7851         this.el.on("contextmenu", this.onContextMenu, this);
7852         
7853         this.mainBody.on('scroll', this.onBodyScroll, this);
7854         
7855         this.cm.on("headerchange", this.onHeaderChange, this);
7856         
7857         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7858         
7859     },
7860     
7861     onContextMenu : function(e, t)
7862     {
7863         this.processEvent("contextmenu", e);
7864     },
7865     
7866     processEvent : function(name, e)
7867     {
7868         if (name != 'touchstart' ) {
7869             this.fireEvent(name, e);    
7870         }
7871         
7872         var t = e.getTarget();
7873         
7874         var cell = Roo.get(t);
7875         
7876         if(!cell){
7877             return;
7878         }
7879         
7880         if(cell.findParent('tfoot', false, true)){
7881             return;
7882         }
7883         
7884         if(cell.findParent('thead', false, true)){
7885             
7886             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7887                 cell = Roo.get(t).findParent('th', false, true);
7888                 if (!cell) {
7889                     Roo.log("failed to find th in thead?");
7890                     Roo.log(e.getTarget());
7891                     return;
7892                 }
7893             }
7894             
7895             var cellIndex = cell.dom.cellIndex;
7896             
7897             var ename = name == 'touchstart' ? 'click' : name;
7898             this.fireEvent("header" + ename, this, cellIndex, e);
7899             
7900             return;
7901         }
7902         
7903         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7904             cell = Roo.get(t).findParent('td', false, true);
7905             if (!cell) {
7906                 Roo.log("failed to find th in tbody?");
7907                 Roo.log(e.getTarget());
7908                 return;
7909             }
7910         }
7911         
7912         var row = cell.findParent('tr', false, true);
7913         var cellIndex = cell.dom.cellIndex;
7914         var rowIndex = row.dom.rowIndex - 1;
7915         
7916         if(row !== false){
7917             
7918             this.fireEvent("row" + name, this, rowIndex, e);
7919             
7920             if(cell !== false){
7921             
7922                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7923             }
7924         }
7925         
7926     },
7927     
7928     onMouseover : function(e, el)
7929     {
7930         var cell = Roo.get(el);
7931         
7932         if(!cell){
7933             return;
7934         }
7935         
7936         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7937             cell = cell.findParent('td', false, true);
7938         }
7939         
7940         var row = cell.findParent('tr', false, true);
7941         var cellIndex = cell.dom.cellIndex;
7942         var rowIndex = row.dom.rowIndex - 1; // start from 0
7943         
7944         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7945         
7946     },
7947     
7948     onMouseout : function(e, el)
7949     {
7950         var cell = Roo.get(el);
7951         
7952         if(!cell){
7953             return;
7954         }
7955         
7956         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7957             cell = cell.findParent('td', false, true);
7958         }
7959         
7960         var row = cell.findParent('tr', false, true);
7961         var cellIndex = cell.dom.cellIndex;
7962         var rowIndex = row.dom.rowIndex - 1; // start from 0
7963         
7964         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7965         
7966     },
7967     
7968     onClick : function(e, el)
7969     {
7970         var cell = Roo.get(el);
7971         
7972         if(!cell || (!this.cellSelection && !this.rowSelection)){
7973             return;
7974         }
7975         
7976         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7977             cell = cell.findParent('td', false, true);
7978         }
7979         
7980         if(!cell || typeof(cell) == 'undefined'){
7981             return;
7982         }
7983         
7984         var row = cell.findParent('tr', false, true);
7985         
7986         if(!row || typeof(row) == 'undefined'){
7987             return;
7988         }
7989         
7990         var cellIndex = cell.dom.cellIndex;
7991         var rowIndex = this.getRowIndex(row);
7992         
7993         // why??? - should these not be based on SelectionModel?
7994         if(this.cellSelection){
7995             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7996         }
7997         
7998         if(this.rowSelection){
7999             this.fireEvent('rowclick', this, row, rowIndex, e);
8000         }
8001         
8002         
8003     },
8004         
8005     onDblClick : function(e,el)
8006     {
8007         var cell = Roo.get(el);
8008         
8009         if(!cell || (!this.cellSelection && !this.rowSelection)){
8010             return;
8011         }
8012         
8013         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8014             cell = cell.findParent('td', false, true);
8015         }
8016         
8017         if(!cell || typeof(cell) == 'undefined'){
8018             return;
8019         }
8020         
8021         var row = cell.findParent('tr', false, true);
8022         
8023         if(!row || typeof(row) == 'undefined'){
8024             return;
8025         }
8026         
8027         var cellIndex = cell.dom.cellIndex;
8028         var rowIndex = this.getRowIndex(row);
8029         
8030         if(this.cellSelection){
8031             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8032         }
8033         
8034         if(this.rowSelection){
8035             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8036         }
8037     },
8038     
8039     sort : function(e,el)
8040     {
8041         var col = Roo.get(el);
8042         
8043         if(!col.hasClass('sortable')){
8044             return;
8045         }
8046         
8047         var sort = col.attr('sort');
8048         var dir = 'ASC';
8049         
8050         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8051             dir = 'DESC';
8052         }
8053         
8054         this.store.sortInfo = {field : sort, direction : dir};
8055         
8056         if (this.footer) {
8057             Roo.log("calling footer first");
8058             this.footer.onClick('first');
8059         } else {
8060         
8061             this.store.load({ params : { start : 0 } });
8062         }
8063     },
8064     
8065     renderHeader : function()
8066     {
8067         var header = {
8068             tag: 'thead',
8069             cn : []
8070         };
8071         
8072         var cm = this.cm;
8073         this.totalWidth = 0;
8074         
8075         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8076             
8077             var config = cm.config[i];
8078             
8079             var c = {
8080                 tag: 'th',
8081                 cls : 'x-hcol-' + i,
8082                 style : '',
8083                 html: cm.getColumnHeader(i)
8084             };
8085             
8086             var hh = '';
8087             
8088             if(typeof(config.sortable) != 'undefined' && config.sortable){
8089                 c.cls = 'sortable';
8090                 c.html = '<i class="glyphicon"></i>' + c.html;
8091             }
8092             
8093             // could use BS4 hidden-..-down 
8094             
8095             if(typeof(config.lgHeader) != 'undefined'){
8096                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8097             }
8098             
8099             if(typeof(config.mdHeader) != 'undefined'){
8100                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8101             }
8102             
8103             if(typeof(config.smHeader) != 'undefined'){
8104                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8105             }
8106             
8107             if(typeof(config.xsHeader) != 'undefined'){
8108                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8109             }
8110             
8111             if(hh.length){
8112                 c.html = hh;
8113             }
8114             
8115             if(typeof(config.tooltip) != 'undefined'){
8116                 c.tooltip = config.tooltip;
8117             }
8118             
8119             if(typeof(config.colspan) != 'undefined'){
8120                 c.colspan = config.colspan;
8121             }
8122             
8123             if(typeof(config.hidden) != 'undefined' && config.hidden){
8124                 c.style += ' display:none;';
8125             }
8126             
8127             if(typeof(config.dataIndex) != 'undefined'){
8128                 c.sort = config.dataIndex;
8129             }
8130             
8131            
8132             
8133             if(typeof(config.align) != 'undefined' && config.align.length){
8134                 c.style += ' text-align:' + config.align + ';';
8135             }
8136             
8137             if(typeof(config.width) != 'undefined'){
8138                 c.style += ' width:' + config.width + 'px;';
8139                 this.totalWidth += config.width;
8140             } else {
8141                 this.totalWidth += 100; // assume minimum of 100 per column?
8142             }
8143             
8144             if(typeof(config.cls) != 'undefined'){
8145                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8146             }
8147             
8148             ['xs','sm','md','lg'].map(function(size){
8149                 
8150                 if(typeof(config[size]) == 'undefined'){
8151                     return;
8152                 }
8153                  
8154                 if (!config[size]) { // 0 = hidden
8155                     // BS 4 '0' is treated as hide that column and below.
8156                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8157                     return;
8158                 }
8159                 
8160                 c.cls += ' col-' + size + '-' + config[size] + (
8161                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8162                 );
8163                 
8164                 
8165             });
8166             
8167             header.cn.push(c)
8168         }
8169         
8170         return header;
8171     },
8172     
8173     renderBody : function()
8174     {
8175         var body = {
8176             tag: 'tbody',
8177             cn : [
8178                 {
8179                     tag: 'tr',
8180                     cn : [
8181                         {
8182                             tag : 'td',
8183                             colspan :  this.cm.getColumnCount()
8184                         }
8185                     ]
8186                 }
8187             ]
8188         };
8189         
8190         return body;
8191     },
8192     
8193     renderFooter : function()
8194     {
8195         var footer = {
8196             tag: 'tfoot',
8197             cn : [
8198                 {
8199                     tag: 'tr',
8200                     cn : [
8201                         {
8202                             tag : 'td',
8203                             colspan :  this.cm.getColumnCount()
8204                         }
8205                     ]
8206                 }
8207             ]
8208         };
8209         
8210         return footer;
8211     },
8212     
8213     
8214     
8215     onLoad : function()
8216     {
8217 //        Roo.log('ds onload');
8218         this.clear();
8219         
8220         var _this = this;
8221         var cm = this.cm;
8222         var ds = this.store;
8223         
8224         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8225             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8226             if (_this.store.sortInfo) {
8227                     
8228                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8229                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8230                 }
8231                 
8232                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8233                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8234                 }
8235             }
8236         });
8237         
8238         var tbody =  this.mainBody;
8239               
8240         if(ds.getCount() > 0){
8241             ds.data.each(function(d,rowIndex){
8242                 var row =  this.renderRow(cm, ds, rowIndex);
8243                 
8244                 tbody.createChild(row);
8245                 
8246                 var _this = this;
8247                 
8248                 if(row.cellObjects.length){
8249                     Roo.each(row.cellObjects, function(r){
8250                         _this.renderCellObject(r);
8251                     })
8252                 }
8253                 
8254             }, this);
8255         }
8256         
8257         var tfoot = this.el.select('tfoot', true).first();
8258         
8259         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8260             
8261             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8262             
8263             var total = this.ds.getTotalCount();
8264             
8265             if(this.footer.pageSize < total){
8266                 this.mainFoot.show();
8267             }
8268         }
8269         
8270         Roo.each(this.el.select('tbody td', true).elements, function(e){
8271             e.on('mouseover', _this.onMouseover, _this);
8272         });
8273         
8274         Roo.each(this.el.select('tbody td', true).elements, function(e){
8275             e.on('mouseout', _this.onMouseout, _this);
8276         });
8277         this.fireEvent('rowsrendered', this);
8278         
8279         this.autoSize();
8280     },
8281     
8282     
8283     onUpdate : function(ds,record)
8284     {
8285         this.refreshRow(record);
8286         this.autoSize();
8287     },
8288     
8289     onRemove : function(ds, record, index, isUpdate){
8290         if(isUpdate !== true){
8291             this.fireEvent("beforerowremoved", this, index, record);
8292         }
8293         var bt = this.mainBody.dom;
8294         
8295         var rows = this.el.select('tbody > tr', true).elements;
8296         
8297         if(typeof(rows[index]) != 'undefined'){
8298             bt.removeChild(rows[index].dom);
8299         }
8300         
8301 //        if(bt.rows[index]){
8302 //            bt.removeChild(bt.rows[index]);
8303 //        }
8304         
8305         if(isUpdate !== true){
8306             //this.stripeRows(index);
8307             //this.syncRowHeights(index, index);
8308             //this.layout();
8309             this.fireEvent("rowremoved", this, index, record);
8310         }
8311     },
8312     
8313     onAdd : function(ds, records, rowIndex)
8314     {
8315         //Roo.log('on Add called');
8316         // - note this does not handle multiple adding very well..
8317         var bt = this.mainBody.dom;
8318         for (var i =0 ; i < records.length;i++) {
8319             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8320             //Roo.log(records[i]);
8321             //Roo.log(this.store.getAt(rowIndex+i));
8322             this.insertRow(this.store, rowIndex + i, false);
8323             return;
8324         }
8325         
8326     },
8327     
8328     
8329     refreshRow : function(record){
8330         var ds = this.store, index;
8331         if(typeof record == 'number'){
8332             index = record;
8333             record = ds.getAt(index);
8334         }else{
8335             index = ds.indexOf(record);
8336             if (index < 0) {
8337                 return; // should not happen - but seems to 
8338             }
8339         }
8340         this.insertRow(ds, index, true);
8341         this.autoSize();
8342         this.onRemove(ds, record, index+1, true);
8343         this.autoSize();
8344         //this.syncRowHeights(index, index);
8345         //this.layout();
8346         this.fireEvent("rowupdated", this, index, record);
8347     },
8348     
8349     insertRow : function(dm, rowIndex, isUpdate){
8350         
8351         if(!isUpdate){
8352             this.fireEvent("beforerowsinserted", this, rowIndex);
8353         }
8354             //var s = this.getScrollState();
8355         var row = this.renderRow(this.cm, this.store, rowIndex);
8356         // insert before rowIndex..
8357         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8358         
8359         var _this = this;
8360                 
8361         if(row.cellObjects.length){
8362             Roo.each(row.cellObjects, function(r){
8363                 _this.renderCellObject(r);
8364             })
8365         }
8366             
8367         if(!isUpdate){
8368             this.fireEvent("rowsinserted", this, rowIndex);
8369             //this.syncRowHeights(firstRow, lastRow);
8370             //this.stripeRows(firstRow);
8371             //this.layout();
8372         }
8373         
8374     },
8375     
8376     
8377     getRowDom : function(rowIndex)
8378     {
8379         var rows = this.el.select('tbody > tr', true).elements;
8380         
8381         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8382         
8383     },
8384     // returns the object tree for a tr..
8385   
8386     
8387     renderRow : function(cm, ds, rowIndex) 
8388     {
8389         var d = ds.getAt(rowIndex);
8390         
8391         var row = {
8392             tag : 'tr',
8393             cls : 'x-row-' + rowIndex,
8394             cn : []
8395         };
8396             
8397         var cellObjects = [];
8398         
8399         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8400             var config = cm.config[i];
8401             
8402             var renderer = cm.getRenderer(i);
8403             var value = '';
8404             var id = false;
8405             
8406             if(typeof(renderer) !== 'undefined'){
8407                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8408             }
8409             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8410             // and are rendered into the cells after the row is rendered - using the id for the element.
8411             
8412             if(typeof(value) === 'object'){
8413                 id = Roo.id();
8414                 cellObjects.push({
8415                     container : id,
8416                     cfg : value 
8417                 })
8418             }
8419             
8420             var rowcfg = {
8421                 record: d,
8422                 rowIndex : rowIndex,
8423                 colIndex : i,
8424                 rowClass : ''
8425             };
8426
8427             this.fireEvent('rowclass', this, rowcfg);
8428             
8429             var td = {
8430                 tag: 'td',
8431                 cls : rowcfg.rowClass + ' x-col-' + i,
8432                 style: '',
8433                 html: (typeof(value) === 'object') ? '' : value
8434             };
8435             
8436             if (id) {
8437                 td.id = id;
8438             }
8439             
8440             if(typeof(config.colspan) != 'undefined'){
8441                 td.colspan = config.colspan;
8442             }
8443             
8444             if(typeof(config.hidden) != 'undefined' && config.hidden){
8445                 td.style += ' display:none;';
8446             }
8447             
8448             if(typeof(config.align) != 'undefined' && config.align.length){
8449                 td.style += ' text-align:' + config.align + ';';
8450             }
8451             if(typeof(config.valign) != 'undefined' && config.valign.length){
8452                 td.style += ' vertical-align:' + config.valign + ';';
8453             }
8454             
8455             if(typeof(config.width) != 'undefined'){
8456                 td.style += ' width:' +  config.width + 'px;';
8457             }
8458             
8459             if(typeof(config.cursor) != 'undefined'){
8460                 td.style += ' cursor:' +  config.cursor + ';';
8461             }
8462             
8463             if(typeof(config.cls) != 'undefined'){
8464                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8465             }
8466             
8467             ['xs','sm','md','lg'].map(function(size){
8468                 
8469                 if(typeof(config[size]) == 'undefined'){
8470                     return;
8471                 }
8472                 
8473                 
8474                   
8475                 if (!config[size]) { // 0 = hidden
8476                     // BS 4 '0' is treated as hide that column and below.
8477                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8478                     return;
8479                 }
8480                 
8481                 td.cls += ' col-' + size + '-' + config[size] + (
8482                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8483                 );
8484                  
8485
8486             });
8487             
8488             row.cn.push(td);
8489            
8490         }
8491         
8492         row.cellObjects = cellObjects;
8493         
8494         return row;
8495           
8496     },
8497     
8498     
8499     
8500     onBeforeLoad : function()
8501     {
8502         
8503     },
8504      /**
8505      * Remove all rows
8506      */
8507     clear : function()
8508     {
8509         this.el.select('tbody', true).first().dom.innerHTML = '';
8510     },
8511     /**
8512      * Show or hide a row.
8513      * @param {Number} rowIndex to show or hide
8514      * @param {Boolean} state hide
8515      */
8516     setRowVisibility : function(rowIndex, state)
8517     {
8518         var bt = this.mainBody.dom;
8519         
8520         var rows = this.el.select('tbody > tr', true).elements;
8521         
8522         if(typeof(rows[rowIndex]) == 'undefined'){
8523             return;
8524         }
8525         rows[rowIndex].dom.style.display = state ? '' : 'none';
8526     },
8527     
8528     
8529     getSelectionModel : function(){
8530         if(!this.selModel){
8531             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8532         }
8533         return this.selModel;
8534     },
8535     /*
8536      * Render the Roo.bootstrap object from renderder
8537      */
8538     renderCellObject : function(r)
8539     {
8540         var _this = this;
8541         
8542         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8543         
8544         var t = r.cfg.render(r.container);
8545         
8546         if(r.cfg.cn){
8547             Roo.each(r.cfg.cn, function(c){
8548                 var child = {
8549                     container: t.getChildContainer(),
8550                     cfg: c
8551                 };
8552                 _this.renderCellObject(child);
8553             })
8554         }
8555     },
8556     
8557     getRowIndex : function(row)
8558     {
8559         var rowIndex = -1;
8560         
8561         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8562             if(el != row){
8563                 return;
8564             }
8565             
8566             rowIndex = index;
8567         });
8568         
8569         return rowIndex;
8570     },
8571      /**
8572      * Returns the grid's underlying element = used by panel.Grid
8573      * @return {Element} The element
8574      */
8575     getGridEl : function(){
8576         return this.el;
8577     },
8578      /**
8579      * Forces a resize - used by panel.Grid
8580      * @return {Element} The element
8581      */
8582     autoSize : function()
8583     {
8584         //var ctr = Roo.get(this.container.dom.parentElement);
8585         var ctr = Roo.get(this.el.dom);
8586         
8587         var thd = this.getGridEl().select('thead',true).first();
8588         var tbd = this.getGridEl().select('tbody', true).first();
8589         var tfd = this.getGridEl().select('tfoot', true).first();
8590         
8591         var cw = ctr.getWidth();
8592         
8593         if (tbd) {
8594             
8595             tbd.setWidth(ctr.getWidth());
8596             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8597             // this needs fixing for various usage - currently only hydra job advers I think..
8598             //tdb.setHeight(
8599             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8600             //); 
8601             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8602             cw -= barsize;
8603         }
8604         cw = Math.max(cw, this.totalWidth);
8605         this.getGridEl().select('tbody tr',true).setWidth(cw);
8606         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8607         // resize 'expandable coloumn?
8608         
8609         return; // we doe not have a view in this design..
8610         
8611     },
8612     onBodyScroll: function()
8613     {
8614         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8615         if(this.mainHead){
8616             this.mainHead.setStyle({
8617                 'position' : 'relative',
8618                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8619             });
8620         }
8621         
8622         if(this.lazyLoad){
8623             
8624             var scrollHeight = this.mainBody.dom.scrollHeight;
8625             
8626             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8627             
8628             var height = this.mainBody.getHeight();
8629             
8630             if(scrollHeight - height == scrollTop) {
8631                 
8632                 var total = this.ds.getTotalCount();
8633                 
8634                 if(this.footer.cursor + this.footer.pageSize < total){
8635                     
8636                     this.footer.ds.load({
8637                         params : {
8638                             start : this.footer.cursor + this.footer.pageSize,
8639                             limit : this.footer.pageSize
8640                         },
8641                         add : true
8642                     });
8643                 }
8644             }
8645             
8646         }
8647     },
8648     
8649     onHeaderChange : function()
8650     {
8651         var header = this.renderHeader();
8652         var table = this.el.select('table', true).first();
8653         
8654         this.mainHead.remove();
8655         this.mainHead = table.createChild(header, this.mainBody, false);
8656     },
8657     
8658     onHiddenChange : function(colModel, colIndex, hidden)
8659     {
8660         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8661         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8662         
8663         this.CSS.updateRule(thSelector, "display", "");
8664         this.CSS.updateRule(tdSelector, "display", "");
8665         
8666         if(hidden){
8667             this.CSS.updateRule(thSelector, "display", "none");
8668             this.CSS.updateRule(tdSelector, "display", "none");
8669         }
8670         
8671         this.onHeaderChange();
8672         this.onLoad();
8673     },
8674     
8675     setColumnWidth: function(col_index, width)
8676     {
8677         // width = "md-2 xs-2..."
8678         if(!this.colModel.config[col_index]) {
8679             return;
8680         }
8681         
8682         var w = width.split(" ");
8683         
8684         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8685         
8686         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8687         
8688         
8689         for(var j = 0; j < w.length; j++) {
8690             
8691             if(!w[j]) {
8692                 continue;
8693             }
8694             
8695             var size_cls = w[j].split("-");
8696             
8697             if(!Number.isInteger(size_cls[1] * 1)) {
8698                 continue;
8699             }
8700             
8701             if(!this.colModel.config[col_index][size_cls[0]]) {
8702                 continue;
8703             }
8704             
8705             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8706                 continue;
8707             }
8708             
8709             h_row[0].classList.replace(
8710                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8711                 "col-"+size_cls[0]+"-"+size_cls[1]
8712             );
8713             
8714             for(var i = 0; i < rows.length; i++) {
8715                 
8716                 var size_cls = w[j].split("-");
8717                 
8718                 if(!Number.isInteger(size_cls[1] * 1)) {
8719                     continue;
8720                 }
8721                 
8722                 if(!this.colModel.config[col_index][size_cls[0]]) {
8723                     continue;
8724                 }
8725                 
8726                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8727                     continue;
8728                 }
8729                 
8730                 rows[i].classList.replace(
8731                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8732                     "col-"+size_cls[0]+"-"+size_cls[1]
8733                 );
8734             }
8735             
8736             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8737         }
8738     }
8739 });
8740
8741  
8742
8743  /*
8744  * - LGPL
8745  *
8746  * table cell
8747  * 
8748  */
8749
8750 /**
8751  * @class Roo.bootstrap.TableCell
8752  * @extends Roo.bootstrap.Component
8753  * Bootstrap TableCell class
8754  * @cfg {String} html cell contain text
8755  * @cfg {String} cls cell class
8756  * @cfg {String} tag cell tag (td|th) default td
8757  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8758  * @cfg {String} align Aligns the content in a cell
8759  * @cfg {String} axis Categorizes cells
8760  * @cfg {String} bgcolor Specifies the background color of a cell
8761  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8762  * @cfg {Number} colspan Specifies the number of columns a cell should span
8763  * @cfg {String} headers Specifies one or more header cells a cell is related to
8764  * @cfg {Number} height Sets the height of a cell
8765  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8766  * @cfg {Number} rowspan Sets the number of rows a cell should span
8767  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8768  * @cfg {String} valign Vertical aligns the content in a cell
8769  * @cfg {Number} width Specifies the width of a cell
8770  * 
8771  * @constructor
8772  * Create a new TableCell
8773  * @param {Object} config The config object
8774  */
8775
8776 Roo.bootstrap.TableCell = function(config){
8777     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8778 };
8779
8780 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8781     
8782     html: false,
8783     cls: false,
8784     tag: false,
8785     abbr: false,
8786     align: false,
8787     axis: false,
8788     bgcolor: false,
8789     charoff: false,
8790     colspan: false,
8791     headers: false,
8792     height: false,
8793     nowrap: false,
8794     rowspan: false,
8795     scope: false,
8796     valign: false,
8797     width: false,
8798     
8799     
8800     getAutoCreate : function(){
8801         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8802         
8803         cfg = {
8804             tag: 'td'
8805         };
8806         
8807         if(this.tag){
8808             cfg.tag = this.tag;
8809         }
8810         
8811         if (this.html) {
8812             cfg.html=this.html
8813         }
8814         if (this.cls) {
8815             cfg.cls=this.cls
8816         }
8817         if (this.abbr) {
8818             cfg.abbr=this.abbr
8819         }
8820         if (this.align) {
8821             cfg.align=this.align
8822         }
8823         if (this.axis) {
8824             cfg.axis=this.axis
8825         }
8826         if (this.bgcolor) {
8827             cfg.bgcolor=this.bgcolor
8828         }
8829         if (this.charoff) {
8830             cfg.charoff=this.charoff
8831         }
8832         if (this.colspan) {
8833             cfg.colspan=this.colspan
8834         }
8835         if (this.headers) {
8836             cfg.headers=this.headers
8837         }
8838         if (this.height) {
8839             cfg.height=this.height
8840         }
8841         if (this.nowrap) {
8842             cfg.nowrap=this.nowrap
8843         }
8844         if (this.rowspan) {
8845             cfg.rowspan=this.rowspan
8846         }
8847         if (this.scope) {
8848             cfg.scope=this.scope
8849         }
8850         if (this.valign) {
8851             cfg.valign=this.valign
8852         }
8853         if (this.width) {
8854             cfg.width=this.width
8855         }
8856         
8857         
8858         return cfg;
8859     }
8860    
8861 });
8862
8863  
8864
8865  /*
8866  * - LGPL
8867  *
8868  * table row
8869  * 
8870  */
8871
8872 /**
8873  * @class Roo.bootstrap.TableRow
8874  * @extends Roo.bootstrap.Component
8875  * Bootstrap TableRow class
8876  * @cfg {String} cls row class
8877  * @cfg {String} align Aligns the content in a table row
8878  * @cfg {String} bgcolor Specifies a background color for a table row
8879  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8880  * @cfg {String} valign Vertical aligns the content in a table row
8881  * 
8882  * @constructor
8883  * Create a new TableRow
8884  * @param {Object} config The config object
8885  */
8886
8887 Roo.bootstrap.TableRow = function(config){
8888     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8889 };
8890
8891 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8892     
8893     cls: false,
8894     align: false,
8895     bgcolor: false,
8896     charoff: false,
8897     valign: false,
8898     
8899     getAutoCreate : function(){
8900         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8901         
8902         cfg = {
8903             tag: 'tr'
8904         };
8905             
8906         if(this.cls){
8907             cfg.cls = this.cls;
8908         }
8909         if(this.align){
8910             cfg.align = this.align;
8911         }
8912         if(this.bgcolor){
8913             cfg.bgcolor = this.bgcolor;
8914         }
8915         if(this.charoff){
8916             cfg.charoff = this.charoff;
8917         }
8918         if(this.valign){
8919             cfg.valign = this.valign;
8920         }
8921         
8922         return cfg;
8923     }
8924    
8925 });
8926
8927  
8928
8929  /*
8930  * - LGPL
8931  *
8932  * table body
8933  * 
8934  */
8935
8936 /**
8937  * @class Roo.bootstrap.TableBody
8938  * @extends Roo.bootstrap.Component
8939  * Bootstrap TableBody class
8940  * @cfg {String} cls element class
8941  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8942  * @cfg {String} align Aligns the content inside the element
8943  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8944  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8945  * 
8946  * @constructor
8947  * Create a new TableBody
8948  * @param {Object} config The config object
8949  */
8950
8951 Roo.bootstrap.TableBody = function(config){
8952     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8953 };
8954
8955 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8956     
8957     cls: false,
8958     tag: false,
8959     align: false,
8960     charoff: false,
8961     valign: false,
8962     
8963     getAutoCreate : function(){
8964         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8965         
8966         cfg = {
8967             tag: 'tbody'
8968         };
8969             
8970         if (this.cls) {
8971             cfg.cls=this.cls
8972         }
8973         if(this.tag){
8974             cfg.tag = this.tag;
8975         }
8976         
8977         if(this.align){
8978             cfg.align = this.align;
8979         }
8980         if(this.charoff){
8981             cfg.charoff = this.charoff;
8982         }
8983         if(this.valign){
8984             cfg.valign = this.valign;
8985         }
8986         
8987         return cfg;
8988     }
8989     
8990     
8991 //    initEvents : function()
8992 //    {
8993 //        
8994 //        if(!this.store){
8995 //            return;
8996 //        }
8997 //        
8998 //        this.store = Roo.factory(this.store, Roo.data);
8999 //        this.store.on('load', this.onLoad, this);
9000 //        
9001 //        this.store.load();
9002 //        
9003 //    },
9004 //    
9005 //    onLoad: function () 
9006 //    {   
9007 //        this.fireEvent('load', this);
9008 //    }
9009 //    
9010 //   
9011 });
9012
9013  
9014
9015  /*
9016  * Based on:
9017  * Ext JS Library 1.1.1
9018  * Copyright(c) 2006-2007, Ext JS, LLC.
9019  *
9020  * Originally Released Under LGPL - original licence link has changed is not relivant.
9021  *
9022  * Fork - LGPL
9023  * <script type="text/javascript">
9024  */
9025
9026 // as we use this in bootstrap.
9027 Roo.namespace('Roo.form');
9028  /**
9029  * @class Roo.form.Action
9030  * Internal Class used to handle form actions
9031  * @constructor
9032  * @param {Roo.form.BasicForm} el The form element or its id
9033  * @param {Object} config Configuration options
9034  */
9035
9036  
9037  
9038 // define the action interface
9039 Roo.form.Action = function(form, options){
9040     this.form = form;
9041     this.options = options || {};
9042 };
9043 /**
9044  * Client Validation Failed
9045  * @const 
9046  */
9047 Roo.form.Action.CLIENT_INVALID = 'client';
9048 /**
9049  * Server Validation Failed
9050  * @const 
9051  */
9052 Roo.form.Action.SERVER_INVALID = 'server';
9053  /**
9054  * Connect to Server Failed
9055  * @const 
9056  */
9057 Roo.form.Action.CONNECT_FAILURE = 'connect';
9058 /**
9059  * Reading Data from Server Failed
9060  * @const 
9061  */
9062 Roo.form.Action.LOAD_FAILURE = 'load';
9063
9064 Roo.form.Action.prototype = {
9065     type : 'default',
9066     failureType : undefined,
9067     response : undefined,
9068     result : undefined,
9069
9070     // interface method
9071     run : function(options){
9072
9073     },
9074
9075     // interface method
9076     success : function(response){
9077
9078     },
9079
9080     // interface method
9081     handleResponse : function(response){
9082
9083     },
9084
9085     // default connection failure
9086     failure : function(response){
9087         
9088         this.response = response;
9089         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9090         this.form.afterAction(this, false);
9091     },
9092
9093     processResponse : function(response){
9094         this.response = response;
9095         if(!response.responseText){
9096             return true;
9097         }
9098         this.result = this.handleResponse(response);
9099         return this.result;
9100     },
9101
9102     // utility functions used internally
9103     getUrl : function(appendParams){
9104         var url = this.options.url || this.form.url || this.form.el.dom.action;
9105         if(appendParams){
9106             var p = this.getParams();
9107             if(p){
9108                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9109             }
9110         }
9111         return url;
9112     },
9113
9114     getMethod : function(){
9115         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9116     },
9117
9118     getParams : function(){
9119         var bp = this.form.baseParams;
9120         var p = this.options.params;
9121         if(p){
9122             if(typeof p == "object"){
9123                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9124             }else if(typeof p == 'string' && bp){
9125                 p += '&' + Roo.urlEncode(bp);
9126             }
9127         }else if(bp){
9128             p = Roo.urlEncode(bp);
9129         }
9130         return p;
9131     },
9132
9133     createCallback : function(){
9134         return {
9135             success: this.success,
9136             failure: this.failure,
9137             scope: this,
9138             timeout: (this.form.timeout*1000),
9139             upload: this.form.fileUpload ? this.success : undefined
9140         };
9141     }
9142 };
9143
9144 Roo.form.Action.Submit = function(form, options){
9145     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9146 };
9147
9148 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9149     type : 'submit',
9150
9151     haveProgress : false,
9152     uploadComplete : false,
9153     
9154     // uploadProgress indicator.
9155     uploadProgress : function()
9156     {
9157         if (!this.form.progressUrl) {
9158             return;
9159         }
9160         
9161         if (!this.haveProgress) {
9162             Roo.MessageBox.progress("Uploading", "Uploading");
9163         }
9164         if (this.uploadComplete) {
9165            Roo.MessageBox.hide();
9166            return;
9167         }
9168         
9169         this.haveProgress = true;
9170    
9171         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9172         
9173         var c = new Roo.data.Connection();
9174         c.request({
9175             url : this.form.progressUrl,
9176             params: {
9177                 id : uid
9178             },
9179             method: 'GET',
9180             success : function(req){
9181                //console.log(data);
9182                 var rdata = false;
9183                 var edata;
9184                 try  {
9185                    rdata = Roo.decode(req.responseText)
9186                 } catch (e) {
9187                     Roo.log("Invalid data from server..");
9188                     Roo.log(edata);
9189                     return;
9190                 }
9191                 if (!rdata || !rdata.success) {
9192                     Roo.log(rdata);
9193                     Roo.MessageBox.alert(Roo.encode(rdata));
9194                     return;
9195                 }
9196                 var data = rdata.data;
9197                 
9198                 if (this.uploadComplete) {
9199                    Roo.MessageBox.hide();
9200                    return;
9201                 }
9202                    
9203                 if (data){
9204                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9205                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9206                     );
9207                 }
9208                 this.uploadProgress.defer(2000,this);
9209             },
9210        
9211             failure: function(data) {
9212                 Roo.log('progress url failed ');
9213                 Roo.log(data);
9214             },
9215             scope : this
9216         });
9217            
9218     },
9219     
9220     
9221     run : function()
9222     {
9223         // run get Values on the form, so it syncs any secondary forms.
9224         this.form.getValues();
9225         
9226         var o = this.options;
9227         var method = this.getMethod();
9228         var isPost = method == 'POST';
9229         if(o.clientValidation === false || this.form.isValid()){
9230             
9231             if (this.form.progressUrl) {
9232                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9233                     (new Date() * 1) + '' + Math.random());
9234                     
9235             } 
9236             
9237             
9238             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9239                 form:this.form.el.dom,
9240                 url:this.getUrl(!isPost),
9241                 method: method,
9242                 params:isPost ? this.getParams() : null,
9243                 isUpload: this.form.fileUpload,
9244                 formData : this.form.formData
9245             }));
9246             
9247             this.uploadProgress();
9248
9249         }else if (o.clientValidation !== false){ // client validation failed
9250             this.failureType = Roo.form.Action.CLIENT_INVALID;
9251             this.form.afterAction(this, false);
9252         }
9253     },
9254
9255     success : function(response)
9256     {
9257         this.uploadComplete= true;
9258         if (this.haveProgress) {
9259             Roo.MessageBox.hide();
9260         }
9261         
9262         
9263         var result = this.processResponse(response);
9264         if(result === true || result.success){
9265             this.form.afterAction(this, true);
9266             return;
9267         }
9268         if(result.errors){
9269             this.form.markInvalid(result.errors);
9270             this.failureType = Roo.form.Action.SERVER_INVALID;
9271         }
9272         this.form.afterAction(this, false);
9273     },
9274     failure : function(response)
9275     {
9276         this.uploadComplete= true;
9277         if (this.haveProgress) {
9278             Roo.MessageBox.hide();
9279         }
9280         
9281         this.response = response;
9282         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9283         this.form.afterAction(this, false);
9284     },
9285     
9286     handleResponse : function(response){
9287         if(this.form.errorReader){
9288             var rs = this.form.errorReader.read(response);
9289             var errors = [];
9290             if(rs.records){
9291                 for(var i = 0, len = rs.records.length; i < len; i++) {
9292                     var r = rs.records[i];
9293                     errors[i] = r.data;
9294                 }
9295             }
9296             if(errors.length < 1){
9297                 errors = null;
9298             }
9299             return {
9300                 success : rs.success,
9301                 errors : errors
9302             };
9303         }
9304         var ret = false;
9305         try {
9306             ret = Roo.decode(response.responseText);
9307         } catch (e) {
9308             ret = {
9309                 success: false,
9310                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9311                 errors : []
9312             };
9313         }
9314         return ret;
9315         
9316     }
9317 });
9318
9319
9320 Roo.form.Action.Load = function(form, options){
9321     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9322     this.reader = this.form.reader;
9323 };
9324
9325 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9326     type : 'load',
9327
9328     run : function(){
9329         
9330         Roo.Ajax.request(Roo.apply(
9331                 this.createCallback(), {
9332                     method:this.getMethod(),
9333                     url:this.getUrl(false),
9334                     params:this.getParams()
9335         }));
9336     },
9337
9338     success : function(response){
9339         
9340         var result = this.processResponse(response);
9341         if(result === true || !result.success || !result.data){
9342             this.failureType = Roo.form.Action.LOAD_FAILURE;
9343             this.form.afterAction(this, false);
9344             return;
9345         }
9346         this.form.clearInvalid();
9347         this.form.setValues(result.data);
9348         this.form.afterAction(this, true);
9349     },
9350
9351     handleResponse : function(response){
9352         if(this.form.reader){
9353             var rs = this.form.reader.read(response);
9354             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9355             return {
9356                 success : rs.success,
9357                 data : data
9358             };
9359         }
9360         return Roo.decode(response.responseText);
9361     }
9362 });
9363
9364 Roo.form.Action.ACTION_TYPES = {
9365     'load' : Roo.form.Action.Load,
9366     'submit' : Roo.form.Action.Submit
9367 };/*
9368  * - LGPL
9369  *
9370  * form
9371  *
9372  */
9373
9374 /**
9375  * @class Roo.bootstrap.Form
9376  * @extends Roo.bootstrap.Component
9377  * Bootstrap Form class
9378  * @cfg {String} method  GET | POST (default POST)
9379  * @cfg {String} labelAlign top | left (default top)
9380  * @cfg {String} align left  | right - for navbars
9381  * @cfg {Boolean} loadMask load mask when submit (default true)
9382
9383  *
9384  * @constructor
9385  * Create a new Form
9386  * @param {Object} config The config object
9387  */
9388
9389
9390 Roo.bootstrap.Form = function(config){
9391     
9392     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9393     
9394     Roo.bootstrap.Form.popover.apply();
9395     
9396     this.addEvents({
9397         /**
9398          * @event clientvalidation
9399          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9400          * @param {Form} this
9401          * @param {Boolean} valid true if the form has passed client-side validation
9402          */
9403         clientvalidation: true,
9404         /**
9405          * @event beforeaction
9406          * Fires before any action is performed. Return false to cancel the action.
9407          * @param {Form} this
9408          * @param {Action} action The action to be performed
9409          */
9410         beforeaction: true,
9411         /**
9412          * @event actionfailed
9413          * Fires when an action fails.
9414          * @param {Form} this
9415          * @param {Action} action The action that failed
9416          */
9417         actionfailed : true,
9418         /**
9419          * @event actioncomplete
9420          * Fires when an action is completed.
9421          * @param {Form} this
9422          * @param {Action} action The action that completed
9423          */
9424         actioncomplete : true
9425     });
9426 };
9427
9428 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9429
9430      /**
9431      * @cfg {String} method
9432      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9433      */
9434     method : 'POST',
9435     /**
9436      * @cfg {String} url
9437      * The URL to use for form actions if one isn't supplied in the action options.
9438      */
9439     /**
9440      * @cfg {Boolean} fileUpload
9441      * Set to true if this form is a file upload.
9442      */
9443
9444     /**
9445      * @cfg {Object} baseParams
9446      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9447      */
9448
9449     /**
9450      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9451      */
9452     timeout: 30,
9453     /**
9454      * @cfg {Sting} align (left|right) for navbar forms
9455      */
9456     align : 'left',
9457
9458     // private
9459     activeAction : null,
9460
9461     /**
9462      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9463      * element by passing it or its id or mask the form itself by passing in true.
9464      * @type Mixed
9465      */
9466     waitMsgTarget : false,
9467
9468     loadMask : true,
9469     
9470     /**
9471      * @cfg {Boolean} errorMask (true|false) default false
9472      */
9473     errorMask : false,
9474     
9475     /**
9476      * @cfg {Number} maskOffset Default 100
9477      */
9478     maskOffset : 100,
9479     
9480     /**
9481      * @cfg {Boolean} maskBody
9482      */
9483     maskBody : false,
9484
9485     getAutoCreate : function(){
9486
9487         var cfg = {
9488             tag: 'form',
9489             method : this.method || 'POST',
9490             id : this.id || Roo.id(),
9491             cls : ''
9492         };
9493         if (this.parent().xtype.match(/^Nav/)) {
9494             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9495
9496         }
9497
9498         if (this.labelAlign == 'left' ) {
9499             cfg.cls += ' form-horizontal';
9500         }
9501
9502
9503         return cfg;
9504     },
9505     initEvents : function()
9506     {
9507         this.el.on('submit', this.onSubmit, this);
9508         // this was added as random key presses on the form where triggering form submit.
9509         this.el.on('keypress', function(e) {
9510             if (e.getCharCode() != 13) {
9511                 return true;
9512             }
9513             // we might need to allow it for textareas.. and some other items.
9514             // check e.getTarget().
9515
9516             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9517                 return true;
9518             }
9519
9520             Roo.log("keypress blocked");
9521
9522             e.preventDefault();
9523             return false;
9524         });
9525         
9526     },
9527     // private
9528     onSubmit : function(e){
9529         e.stopEvent();
9530     },
9531
9532      /**
9533      * Returns true if client-side validation on the form is successful.
9534      * @return Boolean
9535      */
9536     isValid : function(){
9537         var items = this.getItems();
9538         var valid = true;
9539         var target = false;
9540         
9541         items.each(function(f){
9542             
9543             if(f.validate()){
9544                 return;
9545             }
9546             
9547             Roo.log('invalid field: ' + f.name);
9548             
9549             valid = false;
9550
9551             if(!target && f.el.isVisible(true)){
9552                 target = f;
9553             }
9554            
9555         });
9556         
9557         if(this.errorMask && !valid){
9558             Roo.bootstrap.Form.popover.mask(this, target);
9559         }
9560         
9561         return valid;
9562     },
9563     
9564     /**
9565      * Returns true if any fields in this form have changed since their original load.
9566      * @return Boolean
9567      */
9568     isDirty : function(){
9569         var dirty = false;
9570         var items = this.getItems();
9571         items.each(function(f){
9572            if(f.isDirty()){
9573                dirty = true;
9574                return false;
9575            }
9576            return true;
9577         });
9578         return dirty;
9579     },
9580      /**
9581      * Performs a predefined action (submit or load) or custom actions you define on this form.
9582      * @param {String} actionName The name of the action type
9583      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9584      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9585      * accept other config options):
9586      * <pre>
9587 Property          Type             Description
9588 ----------------  ---------------  ----------------------------------------------------------------------------------
9589 url               String           The url for the action (defaults to the form's url)
9590 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9591 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9592 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9593                                    validate the form on the client (defaults to false)
9594      * </pre>
9595      * @return {BasicForm} this
9596      */
9597     doAction : function(action, options){
9598         if(typeof action == 'string'){
9599             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9600         }
9601         if(this.fireEvent('beforeaction', this, action) !== false){
9602             this.beforeAction(action);
9603             action.run.defer(100, action);
9604         }
9605         return this;
9606     },
9607
9608     // private
9609     beforeAction : function(action){
9610         var o = action.options;
9611         
9612         if(this.loadMask){
9613             
9614             if(this.maskBody){
9615                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9616             } else {
9617                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9618             }
9619         }
9620         // not really supported yet.. ??
9621
9622         //if(this.waitMsgTarget === true){
9623         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9624         //}else if(this.waitMsgTarget){
9625         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9626         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9627         //}else {
9628         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9629        // }
9630
9631     },
9632
9633     // private
9634     afterAction : function(action, success){
9635         this.activeAction = null;
9636         var o = action.options;
9637
9638         if(this.loadMask){
9639             
9640             if(this.maskBody){
9641                 Roo.get(document.body).unmask();
9642             } else {
9643                 this.el.unmask();
9644             }
9645         }
9646         
9647         //if(this.waitMsgTarget === true){
9648 //            this.el.unmask();
9649         //}else if(this.waitMsgTarget){
9650         //    this.waitMsgTarget.unmask();
9651         //}else{
9652         //    Roo.MessageBox.updateProgress(1);
9653         //    Roo.MessageBox.hide();
9654        // }
9655         //
9656         if(success){
9657             if(o.reset){
9658                 this.reset();
9659             }
9660             Roo.callback(o.success, o.scope, [this, action]);
9661             this.fireEvent('actioncomplete', this, action);
9662
9663         }else{
9664
9665             // failure condition..
9666             // we have a scenario where updates need confirming.
9667             // eg. if a locking scenario exists..
9668             // we look for { errors : { needs_confirm : true }} in the response.
9669             if (
9670                 (typeof(action.result) != 'undefined')  &&
9671                 (typeof(action.result.errors) != 'undefined')  &&
9672                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9673            ){
9674                 var _t = this;
9675                 Roo.log("not supported yet");
9676                  /*
9677
9678                 Roo.MessageBox.confirm(
9679                     "Change requires confirmation",
9680                     action.result.errorMsg,
9681                     function(r) {
9682                         if (r != 'yes') {
9683                             return;
9684                         }
9685                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9686                     }
9687
9688                 );
9689                 */
9690
9691
9692                 return;
9693             }
9694
9695             Roo.callback(o.failure, o.scope, [this, action]);
9696             // show an error message if no failed handler is set..
9697             if (!this.hasListener('actionfailed')) {
9698                 Roo.log("need to add dialog support");
9699                 /*
9700                 Roo.MessageBox.alert("Error",
9701                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9702                         action.result.errorMsg :
9703                         "Saving Failed, please check your entries or try again"
9704                 );
9705                 */
9706             }
9707
9708             this.fireEvent('actionfailed', this, action);
9709         }
9710
9711     },
9712     /**
9713      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9714      * @param {String} id The value to search for
9715      * @return Field
9716      */
9717     findField : function(id){
9718         var items = this.getItems();
9719         var field = items.get(id);
9720         if(!field){
9721              items.each(function(f){
9722                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9723                     field = f;
9724                     return false;
9725                 }
9726                 return true;
9727             });
9728         }
9729         return field || null;
9730     },
9731      /**
9732      * Mark fields in this form invalid in bulk.
9733      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9734      * @return {BasicForm} this
9735      */
9736     markInvalid : function(errors){
9737         if(errors instanceof Array){
9738             for(var i = 0, len = errors.length; i < len; i++){
9739                 var fieldError = errors[i];
9740                 var f = this.findField(fieldError.id);
9741                 if(f){
9742                     f.markInvalid(fieldError.msg);
9743                 }
9744             }
9745         }else{
9746             var field, id;
9747             for(id in errors){
9748                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9749                     field.markInvalid(errors[id]);
9750                 }
9751             }
9752         }
9753         //Roo.each(this.childForms || [], function (f) {
9754         //    f.markInvalid(errors);
9755         //});
9756
9757         return this;
9758     },
9759
9760     /**
9761      * Set values for fields in this form in bulk.
9762      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9763      * @return {BasicForm} this
9764      */
9765     setValues : function(values){
9766         if(values instanceof Array){ // array of objects
9767             for(var i = 0, len = values.length; i < len; i++){
9768                 var v = values[i];
9769                 var f = this.findField(v.id);
9770                 if(f){
9771                     f.setValue(v.value);
9772                     if(this.trackResetOnLoad){
9773                         f.originalValue = f.getValue();
9774                     }
9775                 }
9776             }
9777         }else{ // object hash
9778             var field, id;
9779             for(id in values){
9780                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9781
9782                     if (field.setFromData &&
9783                         field.valueField &&
9784                         field.displayField &&
9785                         // combos' with local stores can
9786                         // be queried via setValue()
9787                         // to set their value..
9788                         (field.store && !field.store.isLocal)
9789                         ) {
9790                         // it's a combo
9791                         var sd = { };
9792                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9793                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9794                         field.setFromData(sd);
9795
9796                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9797                         
9798                         field.setFromData(values);
9799                         
9800                     } else {
9801                         field.setValue(values[id]);
9802                     }
9803
9804
9805                     if(this.trackResetOnLoad){
9806                         field.originalValue = field.getValue();
9807                     }
9808                 }
9809             }
9810         }
9811
9812         //Roo.each(this.childForms || [], function (f) {
9813         //    f.setValues(values);
9814         //});
9815
9816         return this;
9817     },
9818
9819     /**
9820      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9821      * they are returned as an array.
9822      * @param {Boolean} asString
9823      * @return {Object}
9824      */
9825     getValues : function(asString){
9826         //if (this.childForms) {
9827             // copy values from the child forms
9828         //    Roo.each(this.childForms, function (f) {
9829         //        this.setValues(f.getValues());
9830         //    }, this);
9831         //}
9832
9833
9834
9835         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9836         if(asString === true){
9837             return fs;
9838         }
9839         return Roo.urlDecode(fs);
9840     },
9841
9842     /**
9843      * Returns the fields in this form as an object with key/value pairs.
9844      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9845      * @return {Object}
9846      */
9847     getFieldValues : function(with_hidden)
9848     {
9849         var items = this.getItems();
9850         var ret = {};
9851         items.each(function(f){
9852             
9853             if (!f.getName()) {
9854                 return;
9855             }
9856             
9857             var v = f.getValue();
9858             
9859             if (f.inputType =='radio') {
9860                 if (typeof(ret[f.getName()]) == 'undefined') {
9861                     ret[f.getName()] = ''; // empty..
9862                 }
9863
9864                 if (!f.el.dom.checked) {
9865                     return;
9866
9867                 }
9868                 v = f.el.dom.value;
9869
9870             }
9871             
9872             if(f.xtype == 'MoneyField'){
9873                 ret[f.currencyName] = f.getCurrency();
9874             }
9875
9876             // not sure if this supported any more..
9877             if ((typeof(v) == 'object') && f.getRawValue) {
9878                 v = f.getRawValue() ; // dates..
9879             }
9880             // combo boxes where name != hiddenName...
9881             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9882                 ret[f.name] = f.getRawValue();
9883             }
9884             ret[f.getName()] = v;
9885         });
9886
9887         return ret;
9888     },
9889
9890     /**
9891      * Clears all invalid messages in this form.
9892      * @return {BasicForm} this
9893      */
9894     clearInvalid : function(){
9895         var items = this.getItems();
9896
9897         items.each(function(f){
9898            f.clearInvalid();
9899         });
9900
9901         return this;
9902     },
9903
9904     /**
9905      * Resets this form.
9906      * @return {BasicForm} this
9907      */
9908     reset : function(){
9909         var items = this.getItems();
9910         items.each(function(f){
9911             f.reset();
9912         });
9913
9914         Roo.each(this.childForms || [], function (f) {
9915             f.reset();
9916         });
9917
9918
9919         return this;
9920     },
9921     
9922     getItems : function()
9923     {
9924         var r=new Roo.util.MixedCollection(false, function(o){
9925             return o.id || (o.id = Roo.id());
9926         });
9927         var iter = function(el) {
9928             if (el.inputEl) {
9929                 r.add(el);
9930             }
9931             if (!el.items) {
9932                 return;
9933             }
9934             Roo.each(el.items,function(e) {
9935                 iter(e);
9936             });
9937         };
9938
9939         iter(this);
9940         return r;
9941     },
9942     
9943     hideFields : function(items)
9944     {
9945         Roo.each(items, function(i){
9946             
9947             var f = this.findField(i);
9948             
9949             if(!f){
9950                 return;
9951             }
9952             
9953             f.hide();
9954             
9955         }, this);
9956     },
9957     
9958     showFields : function(items)
9959     {
9960         Roo.each(items, function(i){
9961             
9962             var f = this.findField(i);
9963             
9964             if(!f){
9965                 return;
9966             }
9967             
9968             f.show();
9969             
9970         }, this);
9971     }
9972
9973 });
9974
9975 Roo.apply(Roo.bootstrap.Form, {
9976     
9977     popover : {
9978         
9979         padding : 5,
9980         
9981         isApplied : false,
9982         
9983         isMasked : false,
9984         
9985         form : false,
9986         
9987         target : false,
9988         
9989         toolTip : false,
9990         
9991         intervalID : false,
9992         
9993         maskEl : false,
9994         
9995         apply : function()
9996         {
9997             if(this.isApplied){
9998                 return;
9999             }
10000             
10001             this.maskEl = {
10002                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10003                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10004                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10005                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10006             };
10007             
10008             this.maskEl.top.enableDisplayMode("block");
10009             this.maskEl.left.enableDisplayMode("block");
10010             this.maskEl.bottom.enableDisplayMode("block");
10011             this.maskEl.right.enableDisplayMode("block");
10012             
10013             this.toolTip = new Roo.bootstrap.Tooltip({
10014                 cls : 'roo-form-error-popover',
10015                 alignment : {
10016                     'left' : ['r-l', [-2,0], 'right'],
10017                     'right' : ['l-r', [2,0], 'left'],
10018                     'bottom' : ['tl-bl', [0,2], 'top'],
10019                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10020                 }
10021             });
10022             
10023             this.toolTip.render(Roo.get(document.body));
10024
10025             this.toolTip.el.enableDisplayMode("block");
10026             
10027             Roo.get(document.body).on('click', function(){
10028                 this.unmask();
10029             }, this);
10030             
10031             Roo.get(document.body).on('touchstart', function(){
10032                 this.unmask();
10033             }, this);
10034             
10035             this.isApplied = true
10036         },
10037         
10038         mask : function(form, target)
10039         {
10040             this.form = form;
10041             
10042             this.target = target;
10043             
10044             if(!this.form.errorMask || !target.el){
10045                 return;
10046             }
10047             
10048             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10049             
10050             Roo.log(scrollable);
10051             
10052             var ot = this.target.el.calcOffsetsTo(scrollable);
10053             
10054             var scrollTo = ot[1] - this.form.maskOffset;
10055             
10056             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10057             
10058             scrollable.scrollTo('top', scrollTo);
10059             
10060             var box = this.target.el.getBox();
10061             Roo.log(box);
10062             var zIndex = Roo.bootstrap.Modal.zIndex++;
10063
10064             
10065             this.maskEl.top.setStyle('position', 'absolute');
10066             this.maskEl.top.setStyle('z-index', zIndex);
10067             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10068             this.maskEl.top.setLeft(0);
10069             this.maskEl.top.setTop(0);
10070             this.maskEl.top.show();
10071             
10072             this.maskEl.left.setStyle('position', 'absolute');
10073             this.maskEl.left.setStyle('z-index', zIndex);
10074             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10075             this.maskEl.left.setLeft(0);
10076             this.maskEl.left.setTop(box.y - this.padding);
10077             this.maskEl.left.show();
10078
10079             this.maskEl.bottom.setStyle('position', 'absolute');
10080             this.maskEl.bottom.setStyle('z-index', zIndex);
10081             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10082             this.maskEl.bottom.setLeft(0);
10083             this.maskEl.bottom.setTop(box.bottom + this.padding);
10084             this.maskEl.bottom.show();
10085
10086             this.maskEl.right.setStyle('position', 'absolute');
10087             this.maskEl.right.setStyle('z-index', zIndex);
10088             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10089             this.maskEl.right.setLeft(box.right + this.padding);
10090             this.maskEl.right.setTop(box.y - this.padding);
10091             this.maskEl.right.show();
10092
10093             this.toolTip.bindEl = this.target.el;
10094
10095             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10096
10097             var tip = this.target.blankText;
10098
10099             if(this.target.getValue() !== '' ) {
10100                 
10101                 if (this.target.invalidText.length) {
10102                     tip = this.target.invalidText;
10103                 } else if (this.target.regexText.length){
10104                     tip = this.target.regexText;
10105                 }
10106             }
10107
10108             this.toolTip.show(tip);
10109
10110             this.intervalID = window.setInterval(function() {
10111                 Roo.bootstrap.Form.popover.unmask();
10112             }, 10000);
10113
10114             window.onwheel = function(){ return false;};
10115             
10116             (function(){ this.isMasked = true; }).defer(500, this);
10117             
10118         },
10119         
10120         unmask : function()
10121         {
10122             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10123                 return;
10124             }
10125             
10126             this.maskEl.top.setStyle('position', 'absolute');
10127             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10128             this.maskEl.top.hide();
10129
10130             this.maskEl.left.setStyle('position', 'absolute');
10131             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10132             this.maskEl.left.hide();
10133
10134             this.maskEl.bottom.setStyle('position', 'absolute');
10135             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10136             this.maskEl.bottom.hide();
10137
10138             this.maskEl.right.setStyle('position', 'absolute');
10139             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10140             this.maskEl.right.hide();
10141             
10142             this.toolTip.hide();
10143             
10144             this.toolTip.el.hide();
10145             
10146             window.onwheel = function(){ return true;};
10147             
10148             if(this.intervalID){
10149                 window.clearInterval(this.intervalID);
10150                 this.intervalID = false;
10151             }
10152             
10153             this.isMasked = false;
10154             
10155         }
10156         
10157     }
10158     
10159 });
10160
10161 /*
10162  * Based on:
10163  * Ext JS Library 1.1.1
10164  * Copyright(c) 2006-2007, Ext JS, LLC.
10165  *
10166  * Originally Released Under LGPL - original licence link has changed is not relivant.
10167  *
10168  * Fork - LGPL
10169  * <script type="text/javascript">
10170  */
10171 /**
10172  * @class Roo.form.VTypes
10173  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10174  * @singleton
10175  */
10176 Roo.form.VTypes = function(){
10177     // closure these in so they are only created once.
10178     var alpha = /^[a-zA-Z_]+$/;
10179     var alphanum = /^[a-zA-Z0-9_]+$/;
10180     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10181     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10182
10183     // All these messages and functions are configurable
10184     return {
10185         /**
10186          * The function used to validate email addresses
10187          * @param {String} value The email address
10188          */
10189         'email' : function(v){
10190             return email.test(v);
10191         },
10192         /**
10193          * The error text to display when the email validation function returns false
10194          * @type String
10195          */
10196         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10197         /**
10198          * The keystroke filter mask to be applied on email input
10199          * @type RegExp
10200          */
10201         'emailMask' : /[a-z0-9_\.\-@]/i,
10202
10203         /**
10204          * The function used to validate URLs
10205          * @param {String} value The URL
10206          */
10207         'url' : function(v){
10208             return url.test(v);
10209         },
10210         /**
10211          * The error text to display when the url validation function returns false
10212          * @type String
10213          */
10214         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10215         
10216         /**
10217          * The function used to validate alpha values
10218          * @param {String} value The value
10219          */
10220         'alpha' : function(v){
10221             return alpha.test(v);
10222         },
10223         /**
10224          * The error text to display when the alpha validation function returns false
10225          * @type String
10226          */
10227         'alphaText' : 'This field should only contain letters and _',
10228         /**
10229          * The keystroke filter mask to be applied on alpha input
10230          * @type RegExp
10231          */
10232         'alphaMask' : /[a-z_]/i,
10233
10234         /**
10235          * The function used to validate alphanumeric values
10236          * @param {String} value The value
10237          */
10238         'alphanum' : function(v){
10239             return alphanum.test(v);
10240         },
10241         /**
10242          * The error text to display when the alphanumeric validation function returns false
10243          * @type String
10244          */
10245         'alphanumText' : 'This field should only contain letters, numbers and _',
10246         /**
10247          * The keystroke filter mask to be applied on alphanumeric input
10248          * @type RegExp
10249          */
10250         'alphanumMask' : /[a-z0-9_]/i
10251     };
10252 }();/*
10253  * - LGPL
10254  *
10255  * Input
10256  * 
10257  */
10258
10259 /**
10260  * @class Roo.bootstrap.Input
10261  * @extends Roo.bootstrap.Component
10262  * Bootstrap Input class
10263  * @cfg {Boolean} disabled is it disabled
10264  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10265  * @cfg {String} name name of the input
10266  * @cfg {string} fieldLabel - the label associated
10267  * @cfg {string} placeholder - placeholder to put in text.
10268  * @cfg {string}  before - input group add on before
10269  * @cfg {string} after - input group add on after
10270  * @cfg {string} size - (lg|sm) or leave empty..
10271  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10272  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10273  * @cfg {Number} md colspan out of 12 for computer-sized screens
10274  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10275  * @cfg {string} value default value of the input
10276  * @cfg {Number} labelWidth set the width of label 
10277  * @cfg {Number} labellg set the width of label (1-12)
10278  * @cfg {Number} labelmd set the width of label (1-12)
10279  * @cfg {Number} labelsm set the width of label (1-12)
10280  * @cfg {Number} labelxs set the width of label (1-12)
10281  * @cfg {String} labelAlign (top|left)
10282  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10283  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10284  * @cfg {String} indicatorpos (left|right) default left
10285  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10286  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10287  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10288
10289  * @cfg {String} align (left|center|right) Default left
10290  * @cfg {Boolean} forceFeedback (true|false) Default false
10291  * 
10292  * @constructor
10293  * Create a new Input
10294  * @param {Object} config The config object
10295  */
10296
10297 Roo.bootstrap.Input = function(config){
10298     
10299     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10300     
10301     this.addEvents({
10302         /**
10303          * @event focus
10304          * Fires when this field receives input focus.
10305          * @param {Roo.form.Field} this
10306          */
10307         focus : true,
10308         /**
10309          * @event blur
10310          * Fires when this field loses input focus.
10311          * @param {Roo.form.Field} this
10312          */
10313         blur : true,
10314         /**
10315          * @event specialkey
10316          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10317          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10318          * @param {Roo.form.Field} this
10319          * @param {Roo.EventObject} e The event object
10320          */
10321         specialkey : true,
10322         /**
10323          * @event change
10324          * Fires just before the field blurs if the field value has changed.
10325          * @param {Roo.form.Field} this
10326          * @param {Mixed} newValue The new value
10327          * @param {Mixed} oldValue The original value
10328          */
10329         change : true,
10330         /**
10331          * @event invalid
10332          * Fires after the field has been marked as invalid.
10333          * @param {Roo.form.Field} this
10334          * @param {String} msg The validation message
10335          */
10336         invalid : true,
10337         /**
10338          * @event valid
10339          * Fires after the field has been validated with no errors.
10340          * @param {Roo.form.Field} this
10341          */
10342         valid : true,
10343          /**
10344          * @event keyup
10345          * Fires after the key up
10346          * @param {Roo.form.Field} this
10347          * @param {Roo.EventObject}  e The event Object
10348          */
10349         keyup : true
10350     });
10351 };
10352
10353 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10354      /**
10355      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10356       automatic validation (defaults to "keyup").
10357      */
10358     validationEvent : "keyup",
10359      /**
10360      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10361      */
10362     validateOnBlur : true,
10363     /**
10364      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10365      */
10366     validationDelay : 250,
10367      /**
10368      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10369      */
10370     focusClass : "x-form-focus",  // not needed???
10371     
10372        
10373     /**
10374      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10375      */
10376     invalidClass : "has-warning",
10377     
10378     /**
10379      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10380      */
10381     validClass : "has-success",
10382     
10383     /**
10384      * @cfg {Boolean} hasFeedback (true|false) default true
10385      */
10386     hasFeedback : true,
10387     
10388     /**
10389      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10390      */
10391     invalidFeedbackClass : "glyphicon-warning-sign",
10392     
10393     /**
10394      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10395      */
10396     validFeedbackClass : "glyphicon-ok",
10397     
10398     /**
10399      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10400      */
10401     selectOnFocus : false,
10402     
10403      /**
10404      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10405      */
10406     maskRe : null,
10407        /**
10408      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10409      */
10410     vtype : null,
10411     
10412       /**
10413      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10414      */
10415     disableKeyFilter : false,
10416     
10417        /**
10418      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10419      */
10420     disabled : false,
10421      /**
10422      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10423      */
10424     allowBlank : true,
10425     /**
10426      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10427      */
10428     blankText : "Please complete this mandatory field",
10429     
10430      /**
10431      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10432      */
10433     minLength : 0,
10434     /**
10435      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10436      */
10437     maxLength : Number.MAX_VALUE,
10438     /**
10439      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10440      */
10441     minLengthText : "The minimum length for this field is {0}",
10442     /**
10443      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10444      */
10445     maxLengthText : "The maximum length for this field is {0}",
10446   
10447     
10448     /**
10449      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10450      * If available, this function will be called only after the basic validators all return true, and will be passed the
10451      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10452      */
10453     validator : null,
10454     /**
10455      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10456      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10457      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10458      */
10459     regex : null,
10460     /**
10461      * @cfg {String} regexText -- Depricated - use Invalid Text
10462      */
10463     regexText : "",
10464     
10465     /**
10466      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10467      */
10468     invalidText : "",
10469     
10470     
10471     
10472     autocomplete: false,
10473     
10474     
10475     fieldLabel : '',
10476     inputType : 'text',
10477     
10478     name : false,
10479     placeholder: false,
10480     before : false,
10481     after : false,
10482     size : false,
10483     hasFocus : false,
10484     preventMark: false,
10485     isFormField : true,
10486     value : '',
10487     labelWidth : 2,
10488     labelAlign : false,
10489     readOnly : false,
10490     align : false,
10491     formatedValue : false,
10492     forceFeedback : false,
10493     
10494     indicatorpos : 'left',
10495     
10496     labellg : 0,
10497     labelmd : 0,
10498     labelsm : 0,
10499     labelxs : 0,
10500     
10501     capture : '',
10502     accept : '',
10503     
10504     parentLabelAlign : function()
10505     {
10506         var parent = this;
10507         while (parent.parent()) {
10508             parent = parent.parent();
10509             if (typeof(parent.labelAlign) !='undefined') {
10510                 return parent.labelAlign;
10511             }
10512         }
10513         return 'left';
10514         
10515     },
10516     
10517     getAutoCreate : function()
10518     {
10519         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10520         
10521         var id = Roo.id();
10522         
10523         var cfg = {};
10524         
10525         if(this.inputType != 'hidden'){
10526             cfg.cls = 'form-group' //input-group
10527         }
10528         
10529         var input =  {
10530             tag: 'input',
10531             id : id,
10532             type : this.inputType,
10533             value : this.value,
10534             cls : 'form-control',
10535             placeholder : this.placeholder || '',
10536             autocomplete : this.autocomplete || 'new-password'
10537         };
10538         if (this.inputType == 'file') {
10539             input.style = 'overflow:hidden'; // why not in CSS?
10540         }
10541         
10542         if(this.capture.length){
10543             input.capture = this.capture;
10544         }
10545         
10546         if(this.accept.length){
10547             input.accept = this.accept + "/*";
10548         }
10549         
10550         if(this.align){
10551             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10552         }
10553         
10554         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10555             input.maxLength = this.maxLength;
10556         }
10557         
10558         if (this.disabled) {
10559             input.disabled=true;
10560         }
10561         
10562         if (this.readOnly) {
10563             input.readonly=true;
10564         }
10565         
10566         if (this.name) {
10567             input.name = this.name;
10568         }
10569         
10570         if (this.size) {
10571             input.cls += ' input-' + this.size;
10572         }
10573         
10574         var settings=this;
10575         ['xs','sm','md','lg'].map(function(size){
10576             if (settings[size]) {
10577                 cfg.cls += ' col-' + size + '-' + settings[size];
10578             }
10579         });
10580         
10581         var inputblock = input;
10582         
10583         var feedback = {
10584             tag: 'span',
10585             cls: 'glyphicon form-control-feedback'
10586         };
10587             
10588         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10589             
10590             inputblock = {
10591                 cls : 'has-feedback',
10592                 cn :  [
10593                     input,
10594                     feedback
10595                 ] 
10596             };  
10597         }
10598         
10599         if (this.before || this.after) {
10600             
10601             inputblock = {
10602                 cls : 'input-group',
10603                 cn :  [] 
10604             };
10605             
10606             if (this.before && typeof(this.before) == 'string') {
10607                 
10608                 inputblock.cn.push({
10609                     tag :'span',
10610                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10611                     html : this.before
10612                 });
10613             }
10614             if (this.before && typeof(this.before) == 'object') {
10615                 this.before = Roo.factory(this.before);
10616                 
10617                 inputblock.cn.push({
10618                     tag :'span',
10619                     cls : 'roo-input-before input-group-prepend   input-group-' +
10620                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10621                 });
10622             }
10623             
10624             inputblock.cn.push(input);
10625             
10626             if (this.after && typeof(this.after) == 'string') {
10627                 inputblock.cn.push({
10628                     tag :'span',
10629                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10630                     html : this.after
10631                 });
10632             }
10633             if (this.after && typeof(this.after) == 'object') {
10634                 this.after = Roo.factory(this.after);
10635                 
10636                 inputblock.cn.push({
10637                     tag :'span',
10638                     cls : 'roo-input-after input-group-append  input-group-' +
10639                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10640                 });
10641             }
10642             
10643             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10644                 inputblock.cls += ' has-feedback';
10645                 inputblock.cn.push(feedback);
10646             }
10647         };
10648         var indicator = {
10649             tag : 'i',
10650             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10651             tooltip : 'This field is required'
10652         };
10653         if (this.allowBlank ) {
10654             indicator.style = this.allowBlank ? ' display:none' : '';
10655         }
10656         if (align ==='left' && this.fieldLabel.length) {
10657             
10658             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10659             
10660             cfg.cn = [
10661                 indicator,
10662                 {
10663                     tag: 'label',
10664                     'for' :  id,
10665                     cls : 'control-label col-form-label',
10666                     html : this.fieldLabel
10667
10668                 },
10669                 {
10670                     cls : "", 
10671                     cn: [
10672                         inputblock
10673                     ]
10674                 }
10675             ];
10676             
10677             var labelCfg = cfg.cn[1];
10678             var contentCfg = cfg.cn[2];
10679             
10680             if(this.indicatorpos == 'right'){
10681                 cfg.cn = [
10682                     {
10683                         tag: 'label',
10684                         'for' :  id,
10685                         cls : 'control-label col-form-label',
10686                         cn : [
10687                             {
10688                                 tag : 'span',
10689                                 html : this.fieldLabel
10690                             },
10691                             indicator
10692                         ]
10693                     },
10694                     {
10695                         cls : "",
10696                         cn: [
10697                             inputblock
10698                         ]
10699                     }
10700
10701                 ];
10702                 
10703                 labelCfg = cfg.cn[0];
10704                 contentCfg = cfg.cn[1];
10705             
10706             }
10707             
10708             if(this.labelWidth > 12){
10709                 labelCfg.style = "width: " + this.labelWidth + 'px';
10710             }
10711             
10712             if(this.labelWidth < 13 && this.labelmd == 0){
10713                 this.labelmd = this.labelWidth;
10714             }
10715             
10716             if(this.labellg > 0){
10717                 labelCfg.cls += ' col-lg-' + this.labellg;
10718                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10719             }
10720             
10721             if(this.labelmd > 0){
10722                 labelCfg.cls += ' col-md-' + this.labelmd;
10723                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10724             }
10725             
10726             if(this.labelsm > 0){
10727                 labelCfg.cls += ' col-sm-' + this.labelsm;
10728                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10729             }
10730             
10731             if(this.labelxs > 0){
10732                 labelCfg.cls += ' col-xs-' + this.labelxs;
10733                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10734             }
10735             
10736             
10737         } else if ( this.fieldLabel.length) {
10738                 
10739             
10740             
10741             cfg.cn = [
10742                 {
10743                     tag : 'i',
10744                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10745                     tooltip : 'This field is required',
10746                     style : this.allowBlank ? ' display:none' : '' 
10747                 },
10748                 {
10749                     tag: 'label',
10750                    //cls : 'input-group-addon',
10751                     html : this.fieldLabel
10752
10753                 },
10754
10755                inputblock
10756
10757            ];
10758            
10759            if(this.indicatorpos == 'right'){
10760        
10761                 cfg.cn = [
10762                     {
10763                         tag: 'label',
10764                        //cls : 'input-group-addon',
10765                         html : this.fieldLabel
10766
10767                     },
10768                     {
10769                         tag : 'i',
10770                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10771                         tooltip : 'This field is required',
10772                         style : this.allowBlank ? ' display:none' : '' 
10773                     },
10774
10775                    inputblock
10776
10777                ];
10778
10779             }
10780
10781         } else {
10782             
10783             cfg.cn = [
10784
10785                     inputblock
10786
10787             ];
10788                 
10789                 
10790         };
10791         
10792         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10793            cfg.cls += ' navbar-form';
10794         }
10795         
10796         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10797             // on BS4 we do this only if not form 
10798             cfg.cls += ' navbar-form';
10799             cfg.tag = 'li';
10800         }
10801         
10802         return cfg;
10803         
10804     },
10805     /**
10806      * return the real input element.
10807      */
10808     inputEl: function ()
10809     {
10810         return this.el.select('input.form-control',true).first();
10811     },
10812     
10813     tooltipEl : function()
10814     {
10815         return this.inputEl();
10816     },
10817     
10818     indicatorEl : function()
10819     {
10820         if (Roo.bootstrap.version == 4) {
10821             return false; // not enabled in v4 yet.
10822         }
10823         
10824         var indicator = this.el.select('i.roo-required-indicator',true).first();
10825         
10826         if(!indicator){
10827             return false;
10828         }
10829         
10830         return indicator;
10831         
10832     },
10833     
10834     setDisabled : function(v)
10835     {
10836         var i  = this.inputEl().dom;
10837         if (!v) {
10838             i.removeAttribute('disabled');
10839             return;
10840             
10841         }
10842         i.setAttribute('disabled','true');
10843     },
10844     initEvents : function()
10845     {
10846           
10847         this.inputEl().on("keydown" , this.fireKey,  this);
10848         this.inputEl().on("focus", this.onFocus,  this);
10849         this.inputEl().on("blur", this.onBlur,  this);
10850         
10851         this.inputEl().relayEvent('keyup', this);
10852         
10853         this.indicator = this.indicatorEl();
10854         
10855         if(this.indicator){
10856             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10857         }
10858  
10859         // reference to original value for reset
10860         this.originalValue = this.getValue();
10861         //Roo.form.TextField.superclass.initEvents.call(this);
10862         if(this.validationEvent == 'keyup'){
10863             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10864             this.inputEl().on('keyup', this.filterValidation, this);
10865         }
10866         else if(this.validationEvent !== false){
10867             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10868         }
10869         
10870         if(this.selectOnFocus){
10871             this.on("focus", this.preFocus, this);
10872             
10873         }
10874         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10875             this.inputEl().on("keypress", this.filterKeys, this);
10876         } else {
10877             this.inputEl().relayEvent('keypress', this);
10878         }
10879        /* if(this.grow){
10880             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
10881             this.el.on("click", this.autoSize,  this);
10882         }
10883         */
10884         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10885             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10886         }
10887         
10888         if (typeof(this.before) == 'object') {
10889             this.before.render(this.el.select('.roo-input-before',true).first());
10890         }
10891         if (typeof(this.after) == 'object') {
10892             this.after.render(this.el.select('.roo-input-after',true).first());
10893         }
10894         
10895         this.inputEl().on('change', this.onChange, this);
10896         
10897     },
10898     filterValidation : function(e){
10899         if(!e.isNavKeyPress()){
10900             this.validationTask.delay(this.validationDelay);
10901         }
10902     },
10903      /**
10904      * Validates the field value
10905      * @return {Boolean} True if the value is valid, else false
10906      */
10907     validate : function(){
10908         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10909         if(this.disabled || this.validateValue(this.getRawValue())){
10910             this.markValid();
10911             return true;
10912         }
10913         
10914         this.markInvalid();
10915         return false;
10916     },
10917     
10918     
10919     /**
10920      * Validates a value according to the field's validation rules and marks the field as invalid
10921      * if the validation fails
10922      * @param {Mixed} value The value to validate
10923      * @return {Boolean} True if the value is valid, else false
10924      */
10925     validateValue : function(value)
10926     {
10927         if(this.getVisibilityEl().hasClass('hidden')){
10928             return true;
10929         }
10930         
10931         if(value.length < 1)  { // if it's blank
10932             if(this.allowBlank){
10933                 return true;
10934             }
10935             return false;
10936         }
10937         
10938         if(value.length < this.minLength){
10939             return false;
10940         }
10941         if(value.length > this.maxLength){
10942             return false;
10943         }
10944         if(this.vtype){
10945             var vt = Roo.form.VTypes;
10946             if(!vt[this.vtype](value, this)){
10947                 return false;
10948             }
10949         }
10950         if(typeof this.validator == "function"){
10951             var msg = this.validator(value);
10952             if(msg !== true){
10953                 return false;
10954             }
10955             if (typeof(msg) == 'string') {
10956                 this.invalidText = msg;
10957             }
10958         }
10959         
10960         if(this.regex && !this.regex.test(value)){
10961             return false;
10962         }
10963         
10964         return true;
10965     },
10966     
10967      // private
10968     fireKey : function(e){
10969         //Roo.log('field ' + e.getKey());
10970         if(e.isNavKeyPress()){
10971             this.fireEvent("specialkey", this, e);
10972         }
10973     },
10974     focus : function (selectText){
10975         if(this.rendered){
10976             this.inputEl().focus();
10977             if(selectText === true){
10978                 this.inputEl().dom.select();
10979             }
10980         }
10981         return this;
10982     } ,
10983     
10984     onFocus : function(){
10985         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10986            // this.el.addClass(this.focusClass);
10987         }
10988         if(!this.hasFocus){
10989             this.hasFocus = true;
10990             this.startValue = this.getValue();
10991             this.fireEvent("focus", this);
10992         }
10993     },
10994     
10995     beforeBlur : Roo.emptyFn,
10996
10997     
10998     // private
10999     onBlur : function(){
11000         this.beforeBlur();
11001         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11002             //this.el.removeClass(this.focusClass);
11003         }
11004         this.hasFocus = false;
11005         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11006             this.validate();
11007         }
11008         var v = this.getValue();
11009         if(String(v) !== String(this.startValue)){
11010             this.fireEvent('change', this, v, this.startValue);
11011         }
11012         this.fireEvent("blur", this);
11013     },
11014     
11015     onChange : function(e)
11016     {
11017         var v = this.getValue();
11018         if(String(v) !== String(this.startValue)){
11019             this.fireEvent('change', this, v, this.startValue);
11020         }
11021         
11022     },
11023     
11024     /**
11025      * Resets the current field value to the originally loaded value and clears any validation messages
11026      */
11027     reset : function(){
11028         this.setValue(this.originalValue);
11029         this.validate();
11030     },
11031      /**
11032      * Returns the name of the field
11033      * @return {Mixed} name The name field
11034      */
11035     getName: function(){
11036         return this.name;
11037     },
11038      /**
11039      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11040      * @return {Mixed} value The field value
11041      */
11042     getValue : function(){
11043         
11044         var v = this.inputEl().getValue();
11045         
11046         return v;
11047     },
11048     /**
11049      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11050      * @return {Mixed} value The field value
11051      */
11052     getRawValue : function(){
11053         var v = this.inputEl().getValue();
11054         
11055         return v;
11056     },
11057     
11058     /**
11059      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11060      * @param {Mixed} value The value to set
11061      */
11062     setRawValue : function(v){
11063         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11064     },
11065     
11066     selectText : function(start, end){
11067         var v = this.getRawValue();
11068         if(v.length > 0){
11069             start = start === undefined ? 0 : start;
11070             end = end === undefined ? v.length : end;
11071             var d = this.inputEl().dom;
11072             if(d.setSelectionRange){
11073                 d.setSelectionRange(start, end);
11074             }else if(d.createTextRange){
11075                 var range = d.createTextRange();
11076                 range.moveStart("character", start);
11077                 range.moveEnd("character", v.length-end);
11078                 range.select();
11079             }
11080         }
11081     },
11082     
11083     /**
11084      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11085      * @param {Mixed} value The value to set
11086      */
11087     setValue : function(v){
11088         this.value = v;
11089         if(this.rendered){
11090             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11091             this.validate();
11092         }
11093     },
11094     
11095     /*
11096     processValue : function(value){
11097         if(this.stripCharsRe){
11098             var newValue = value.replace(this.stripCharsRe, '');
11099             if(newValue !== value){
11100                 this.setRawValue(newValue);
11101                 return newValue;
11102             }
11103         }
11104         return value;
11105     },
11106   */
11107     preFocus : function(){
11108         
11109         if(this.selectOnFocus){
11110             this.inputEl().dom.select();
11111         }
11112     },
11113     filterKeys : function(e){
11114         var k = e.getKey();
11115         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11116             return;
11117         }
11118         var c = e.getCharCode(), cc = String.fromCharCode(c);
11119         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11120             return;
11121         }
11122         if(!this.maskRe.test(cc)){
11123             e.stopEvent();
11124         }
11125     },
11126      /**
11127      * Clear any invalid styles/messages for this field
11128      */
11129     clearInvalid : function(){
11130         
11131         if(!this.el || this.preventMark){ // not rendered
11132             return;
11133         }
11134         
11135         
11136         this.el.removeClass([this.invalidClass, 'is-invalid']);
11137         
11138         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11139             
11140             var feedback = this.el.select('.form-control-feedback', true).first();
11141             
11142             if(feedback){
11143                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11144             }
11145             
11146         }
11147         
11148         if(this.indicator){
11149             this.indicator.removeClass('visible');
11150             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11151         }
11152         
11153         this.fireEvent('valid', this);
11154     },
11155     
11156      /**
11157      * Mark this field as valid
11158      */
11159     markValid : function()
11160     {
11161         if(!this.el  || this.preventMark){ // not rendered...
11162             return;
11163         }
11164         
11165         this.el.removeClass([this.invalidClass, this.validClass]);
11166         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11167
11168         var feedback = this.el.select('.form-control-feedback', true).first();
11169             
11170         if(feedback){
11171             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11172         }
11173         
11174         if(this.indicator){
11175             this.indicator.removeClass('visible');
11176             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11177         }
11178         
11179         if(this.disabled){
11180             return;
11181         }
11182         
11183            
11184         if(this.allowBlank && !this.getRawValue().length){
11185             return;
11186         }
11187         if (Roo.bootstrap.version == 3) {
11188             this.el.addClass(this.validClass);
11189         } else {
11190             this.inputEl().addClass('is-valid');
11191         }
11192
11193         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11194             
11195             var feedback = this.el.select('.form-control-feedback', true).first();
11196             
11197             if(feedback){
11198                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11199                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11200             }
11201             
11202         }
11203         
11204         this.fireEvent('valid', this);
11205     },
11206     
11207      /**
11208      * Mark this field as invalid
11209      * @param {String} msg The validation message
11210      */
11211     markInvalid : function(msg)
11212     {
11213         if(!this.el  || this.preventMark){ // not rendered
11214             return;
11215         }
11216         
11217         this.el.removeClass([this.invalidClass, this.validClass]);
11218         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11219         
11220         var feedback = this.el.select('.form-control-feedback', true).first();
11221             
11222         if(feedback){
11223             this.el.select('.form-control-feedback', true).first().removeClass(
11224                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11225         }
11226
11227         if(this.disabled){
11228             return;
11229         }
11230         
11231         if(this.allowBlank && !this.getRawValue().length){
11232             return;
11233         }
11234         
11235         if(this.indicator){
11236             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11237             this.indicator.addClass('visible');
11238         }
11239         if (Roo.bootstrap.version == 3) {
11240             this.el.addClass(this.invalidClass);
11241         } else {
11242             this.inputEl().addClass('is-invalid');
11243         }
11244         
11245         
11246         
11247         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11248             
11249             var feedback = this.el.select('.form-control-feedback', true).first();
11250             
11251             if(feedback){
11252                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11253                 
11254                 if(this.getValue().length || this.forceFeedback){
11255                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11256                 }
11257                 
11258             }
11259             
11260         }
11261         
11262         this.fireEvent('invalid', this, msg);
11263     },
11264     // private
11265     SafariOnKeyDown : function(event)
11266     {
11267         // this is a workaround for a password hang bug on chrome/ webkit.
11268         if (this.inputEl().dom.type != 'password') {
11269             return;
11270         }
11271         
11272         var isSelectAll = false;
11273         
11274         if(this.inputEl().dom.selectionEnd > 0){
11275             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11276         }
11277         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11278             event.preventDefault();
11279             this.setValue('');
11280             return;
11281         }
11282         
11283         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11284             
11285             event.preventDefault();
11286             // this is very hacky as keydown always get's upper case.
11287             //
11288             var cc = String.fromCharCode(event.getCharCode());
11289             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11290             
11291         }
11292     },
11293     adjustWidth : function(tag, w){
11294         tag = tag.toLowerCase();
11295         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11296             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11297                 if(tag == 'input'){
11298                     return w + 2;
11299                 }
11300                 if(tag == 'textarea'){
11301                     return w-2;
11302                 }
11303             }else if(Roo.isOpera){
11304                 if(tag == 'input'){
11305                     return w + 2;
11306                 }
11307                 if(tag == 'textarea'){
11308                     return w-2;
11309                 }
11310             }
11311         }
11312         return w;
11313     },
11314     
11315     setFieldLabel : function(v)
11316     {
11317         if(!this.rendered){
11318             return;
11319         }
11320         
11321         if(this.indicatorEl()){
11322             var ar = this.el.select('label > span',true);
11323             
11324             if (ar.elements.length) {
11325                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11326                 this.fieldLabel = v;
11327                 return;
11328             }
11329             
11330             var br = this.el.select('label',true);
11331             
11332             if(br.elements.length) {
11333                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11334                 this.fieldLabel = v;
11335                 return;
11336             }
11337             
11338             Roo.log('Cannot Found any of label > span || label in input');
11339             return;
11340         }
11341         
11342         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11343         this.fieldLabel = v;
11344         
11345         
11346     }
11347 });
11348
11349  
11350 /*
11351  * - LGPL
11352  *
11353  * Input
11354  * 
11355  */
11356
11357 /**
11358  * @class Roo.bootstrap.TextArea
11359  * @extends Roo.bootstrap.Input
11360  * Bootstrap TextArea class
11361  * @cfg {Number} cols Specifies the visible width of a text area
11362  * @cfg {Number} rows Specifies the visible number of lines in a text area
11363  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11364  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11365  * @cfg {string} html text
11366  * 
11367  * @constructor
11368  * Create a new TextArea
11369  * @param {Object} config The config object
11370  */
11371
11372 Roo.bootstrap.TextArea = function(config){
11373     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11374    
11375 };
11376
11377 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11378      
11379     cols : false,
11380     rows : 5,
11381     readOnly : false,
11382     warp : 'soft',
11383     resize : false,
11384     value: false,
11385     html: false,
11386     
11387     getAutoCreate : function(){
11388         
11389         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11390         
11391         var id = Roo.id();
11392         
11393         var cfg = {};
11394         
11395         if(this.inputType != 'hidden'){
11396             cfg.cls = 'form-group' //input-group
11397         }
11398         
11399         var input =  {
11400             tag: 'textarea',
11401             id : id,
11402             warp : this.warp,
11403             rows : this.rows,
11404             value : this.value || '',
11405             html: this.html || '',
11406             cls : 'form-control',
11407             placeholder : this.placeholder || '' 
11408             
11409         };
11410         
11411         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11412             input.maxLength = this.maxLength;
11413         }
11414         
11415         if(this.resize){
11416             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11417         }
11418         
11419         if(this.cols){
11420             input.cols = this.cols;
11421         }
11422         
11423         if (this.readOnly) {
11424             input.readonly = true;
11425         }
11426         
11427         if (this.name) {
11428             input.name = this.name;
11429         }
11430         
11431         if (this.size) {
11432             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11433         }
11434         
11435         var settings=this;
11436         ['xs','sm','md','lg'].map(function(size){
11437             if (settings[size]) {
11438                 cfg.cls += ' col-' + size + '-' + settings[size];
11439             }
11440         });
11441         
11442         var inputblock = input;
11443         
11444         if(this.hasFeedback && !this.allowBlank){
11445             
11446             var feedback = {
11447                 tag: 'span',
11448                 cls: 'glyphicon form-control-feedback'
11449             };
11450
11451             inputblock = {
11452                 cls : 'has-feedback',
11453                 cn :  [
11454                     input,
11455                     feedback
11456                 ] 
11457             };  
11458         }
11459         
11460         
11461         if (this.before || this.after) {
11462             
11463             inputblock = {
11464                 cls : 'input-group',
11465                 cn :  [] 
11466             };
11467             if (this.before) {
11468                 inputblock.cn.push({
11469                     tag :'span',
11470                     cls : 'input-group-addon',
11471                     html : this.before
11472                 });
11473             }
11474             
11475             inputblock.cn.push(input);
11476             
11477             if(this.hasFeedback && !this.allowBlank){
11478                 inputblock.cls += ' has-feedback';
11479                 inputblock.cn.push(feedback);
11480             }
11481             
11482             if (this.after) {
11483                 inputblock.cn.push({
11484                     tag :'span',
11485                     cls : 'input-group-addon',
11486                     html : this.after
11487                 });
11488             }
11489             
11490         }
11491         
11492         if (align ==='left' && this.fieldLabel.length) {
11493             cfg.cn = [
11494                 {
11495                     tag: 'label',
11496                     'for' :  id,
11497                     cls : 'control-label',
11498                     html : this.fieldLabel
11499                 },
11500                 {
11501                     cls : "",
11502                     cn: [
11503                         inputblock
11504                     ]
11505                 }
11506
11507             ];
11508             
11509             if(this.labelWidth > 12){
11510                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11511             }
11512
11513             if(this.labelWidth < 13 && this.labelmd == 0){
11514                 this.labelmd = this.labelWidth;
11515             }
11516
11517             if(this.labellg > 0){
11518                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11519                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11520             }
11521
11522             if(this.labelmd > 0){
11523                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11524                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11525             }
11526
11527             if(this.labelsm > 0){
11528                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11529                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11530             }
11531
11532             if(this.labelxs > 0){
11533                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11534                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11535             }
11536             
11537         } else if ( this.fieldLabel.length) {
11538             cfg.cn = [
11539
11540                {
11541                    tag: 'label',
11542                    //cls : 'input-group-addon',
11543                    html : this.fieldLabel
11544
11545                },
11546
11547                inputblock
11548
11549            ];
11550
11551         } else {
11552
11553             cfg.cn = [
11554
11555                 inputblock
11556
11557             ];
11558                 
11559         }
11560         
11561         if (this.disabled) {
11562             input.disabled=true;
11563         }
11564         
11565         return cfg;
11566         
11567     },
11568     /**
11569      * return the real textarea element.
11570      */
11571     inputEl: function ()
11572     {
11573         return this.el.select('textarea.form-control',true).first();
11574     },
11575     
11576     /**
11577      * Clear any invalid styles/messages for this field
11578      */
11579     clearInvalid : function()
11580     {
11581         
11582         if(!this.el || this.preventMark){ // not rendered
11583             return;
11584         }
11585         
11586         var label = this.el.select('label', true).first();
11587         var icon = this.el.select('i.fa-star', true).first();
11588         
11589         if(label && icon){
11590             icon.remove();
11591         }
11592         this.el.removeClass( this.validClass);
11593         this.inputEl().removeClass('is-invalid');
11594          
11595         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11596             
11597             var feedback = this.el.select('.form-control-feedback', true).first();
11598             
11599             if(feedback){
11600                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11601             }
11602             
11603         }
11604         
11605         this.fireEvent('valid', this);
11606     },
11607     
11608      /**
11609      * Mark this field as valid
11610      */
11611     markValid : function()
11612     {
11613         if(!this.el  || this.preventMark){ // not rendered
11614             return;
11615         }
11616         
11617         this.el.removeClass([this.invalidClass, this.validClass]);
11618         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11619         
11620         var feedback = this.el.select('.form-control-feedback', true).first();
11621             
11622         if(feedback){
11623             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11624         }
11625
11626         if(this.disabled || this.allowBlank){
11627             return;
11628         }
11629         
11630         var label = this.el.select('label', true).first();
11631         var icon = this.el.select('i.fa-star', true).first();
11632         
11633         if(label && icon){
11634             icon.remove();
11635         }
11636         if (Roo.bootstrap.version == 3) {
11637             this.el.addClass(this.validClass);
11638         } else {
11639             this.inputEl().addClass('is-valid');
11640         }
11641         
11642         
11643         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11644             
11645             var feedback = this.el.select('.form-control-feedback', true).first();
11646             
11647             if(feedback){
11648                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11649                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11650             }
11651             
11652         }
11653         
11654         this.fireEvent('valid', this);
11655     },
11656     
11657      /**
11658      * Mark this field as invalid
11659      * @param {String} msg The validation message
11660      */
11661     markInvalid : function(msg)
11662     {
11663         if(!this.el  || this.preventMark){ // not rendered
11664             return;
11665         }
11666         
11667         this.el.removeClass([this.invalidClass, this.validClass]);
11668         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11669         
11670         var feedback = this.el.select('.form-control-feedback', true).first();
11671             
11672         if(feedback){
11673             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11674         }
11675
11676         if(this.disabled || this.allowBlank){
11677             return;
11678         }
11679         
11680         var label = this.el.select('label', true).first();
11681         var icon = this.el.select('i.fa-star', true).first();
11682         
11683         if(!this.getValue().length && label && !icon){
11684             this.el.createChild({
11685                 tag : 'i',
11686                 cls : 'text-danger fa fa-lg fa-star',
11687                 tooltip : 'This field is required',
11688                 style : 'margin-right:5px;'
11689             }, label, true);
11690         }
11691         
11692         if (Roo.bootstrap.version == 3) {
11693             this.el.addClass(this.invalidClass);
11694         } else {
11695             this.inputEl().addClass('is-invalid');
11696         }
11697         
11698         // fixme ... this may be depricated need to test..
11699         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11700             
11701             var feedback = this.el.select('.form-control-feedback', true).first();
11702             
11703             if(feedback){
11704                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11705                 
11706                 if(this.getValue().length || this.forceFeedback){
11707                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11708                 }
11709                 
11710             }
11711             
11712         }
11713         
11714         this.fireEvent('invalid', this, msg);
11715     }
11716 });
11717
11718  
11719 /*
11720  * - LGPL
11721  *
11722  * trigger field - base class for combo..
11723  * 
11724  */
11725  
11726 /**
11727  * @class Roo.bootstrap.TriggerField
11728  * @extends Roo.bootstrap.Input
11729  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11730  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11731  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11732  * for which you can provide a custom implementation.  For example:
11733  * <pre><code>
11734 var trigger = new Roo.bootstrap.TriggerField();
11735 trigger.onTriggerClick = myTriggerFn;
11736 trigger.applyTo('my-field');
11737 </code></pre>
11738  *
11739  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11740  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11741  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11742  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11743  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11744
11745  * @constructor
11746  * Create a new TriggerField.
11747  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11748  * to the base TextField)
11749  */
11750 Roo.bootstrap.TriggerField = function(config){
11751     this.mimicing = false;
11752     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11753 };
11754
11755 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11756     /**
11757      * @cfg {String} triggerClass A CSS class to apply to the trigger
11758      */
11759      /**
11760      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11761      */
11762     hideTrigger:false,
11763
11764     /**
11765      * @cfg {Boolean} removable (true|false) special filter default false
11766      */
11767     removable : false,
11768     
11769     /** @cfg {Boolean} grow @hide */
11770     /** @cfg {Number} growMin @hide */
11771     /** @cfg {Number} growMax @hide */
11772
11773     /**
11774      * @hide 
11775      * @method
11776      */
11777     autoSize: Roo.emptyFn,
11778     // private
11779     monitorTab : true,
11780     // private
11781     deferHeight : true,
11782
11783     
11784     actionMode : 'wrap',
11785     
11786     caret : false,
11787     
11788     
11789     getAutoCreate : function(){
11790        
11791         var align = this.labelAlign || this.parentLabelAlign();
11792         
11793         var id = Roo.id();
11794         
11795         var cfg = {
11796             cls: 'form-group' //input-group
11797         };
11798         
11799         
11800         var input =  {
11801             tag: 'input',
11802             id : id,
11803             type : this.inputType,
11804             cls : 'form-control',
11805             autocomplete: 'new-password',
11806             placeholder : this.placeholder || '' 
11807             
11808         };
11809         if (this.name) {
11810             input.name = this.name;
11811         }
11812         if (this.size) {
11813             input.cls += ' input-' + this.size;
11814         }
11815         
11816         if (this.disabled) {
11817             input.disabled=true;
11818         }
11819         
11820         var inputblock = input;
11821         
11822         if(this.hasFeedback && !this.allowBlank){
11823             
11824             var feedback = {
11825                 tag: 'span',
11826                 cls: 'glyphicon form-control-feedback'
11827             };
11828             
11829             if(this.removable && !this.editable  ){
11830                 inputblock = {
11831                     cls : 'has-feedback',
11832                     cn :  [
11833                         inputblock,
11834                         {
11835                             tag: 'button',
11836                             html : 'x',
11837                             cls : 'roo-combo-removable-btn close'
11838                         },
11839                         feedback
11840                     ] 
11841                 };
11842             } else {
11843                 inputblock = {
11844                     cls : 'has-feedback',
11845                     cn :  [
11846                         inputblock,
11847                         feedback
11848                     ] 
11849                 };
11850             }
11851
11852         } else {
11853             if(this.removable && !this.editable ){
11854                 inputblock = {
11855                     cls : 'roo-removable',
11856                     cn :  [
11857                         inputblock,
11858                         {
11859                             tag: 'button',
11860                             html : 'x',
11861                             cls : 'roo-combo-removable-btn close'
11862                         }
11863                     ] 
11864                 };
11865             }
11866         }
11867         
11868         if (this.before || this.after) {
11869             
11870             inputblock = {
11871                 cls : 'input-group',
11872                 cn :  [] 
11873             };
11874             if (this.before) {
11875                 inputblock.cn.push({
11876                     tag :'span',
11877                     cls : 'input-group-addon input-group-prepend input-group-text',
11878                     html : this.before
11879                 });
11880             }
11881             
11882             inputblock.cn.push(input);
11883             
11884             if(this.hasFeedback && !this.allowBlank){
11885                 inputblock.cls += ' has-feedback';
11886                 inputblock.cn.push(feedback);
11887             }
11888             
11889             if (this.after) {
11890                 inputblock.cn.push({
11891                     tag :'span',
11892                     cls : 'input-group-addon input-group-append input-group-text',
11893                     html : this.after
11894                 });
11895             }
11896             
11897         };
11898         
11899       
11900         
11901         var ibwrap = inputblock;
11902         
11903         if(this.multiple){
11904             ibwrap = {
11905                 tag: 'ul',
11906                 cls: 'roo-select2-choices',
11907                 cn:[
11908                     {
11909                         tag: 'li',
11910                         cls: 'roo-select2-search-field',
11911                         cn: [
11912
11913                             inputblock
11914                         ]
11915                     }
11916                 ]
11917             };
11918                 
11919         }
11920         
11921         var combobox = {
11922             cls: 'roo-select2-container input-group',
11923             cn: [
11924                  {
11925                     tag: 'input',
11926                     type : 'hidden',
11927                     cls: 'form-hidden-field'
11928                 },
11929                 ibwrap
11930             ]
11931         };
11932         
11933         if(!this.multiple && this.showToggleBtn){
11934             
11935             var caret = {
11936                         tag: 'span',
11937                         cls: 'caret'
11938              };
11939             if (this.caret != false) {
11940                 caret = {
11941                      tag: 'i',
11942                      cls: 'fa fa-' + this.caret
11943                 };
11944                 
11945             }
11946             
11947             combobox.cn.push({
11948                 tag :'span',
11949                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11950                 cn : [
11951                     Roo.bootstrap.version == 3 ? caret : '',
11952                     {
11953                         tag: 'span',
11954                         cls: 'combobox-clear',
11955                         cn  : [
11956                             {
11957                                 tag : 'i',
11958                                 cls: 'icon-remove'
11959                             }
11960                         ]
11961                     }
11962                 ]
11963
11964             })
11965         }
11966         
11967         if(this.multiple){
11968             combobox.cls += ' roo-select2-container-multi';
11969         }
11970          var indicator = {
11971             tag : 'i',
11972             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11973             tooltip : 'This field is required'
11974         };
11975         if (Roo.bootstrap.version == 4) {
11976             indicator = {
11977                 tag : 'i',
11978                 style : 'display:none'
11979             };
11980         }
11981         
11982         
11983         if (align ==='left' && this.fieldLabel.length) {
11984             
11985             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11986
11987             cfg.cn = [
11988                 indicator,
11989                 {
11990                     tag: 'label',
11991                     'for' :  id,
11992                     cls : 'control-label',
11993                     html : this.fieldLabel
11994
11995                 },
11996                 {
11997                     cls : "", 
11998                     cn: [
11999                         combobox
12000                     ]
12001                 }
12002
12003             ];
12004             
12005             var labelCfg = cfg.cn[1];
12006             var contentCfg = cfg.cn[2];
12007             
12008             if(this.indicatorpos == 'right'){
12009                 cfg.cn = [
12010                     {
12011                         tag: 'label',
12012                         'for' :  id,
12013                         cls : 'control-label',
12014                         cn : [
12015                             {
12016                                 tag : 'span',
12017                                 html : this.fieldLabel
12018                             },
12019                             indicator
12020                         ]
12021                     },
12022                     {
12023                         cls : "", 
12024                         cn: [
12025                             combobox
12026                         ]
12027                     }
12028
12029                 ];
12030                 
12031                 labelCfg = cfg.cn[0];
12032                 contentCfg = cfg.cn[1];
12033             }
12034             
12035             if(this.labelWidth > 12){
12036                 labelCfg.style = "width: " + this.labelWidth + 'px';
12037             }
12038             
12039             if(this.labelWidth < 13 && this.labelmd == 0){
12040                 this.labelmd = this.labelWidth;
12041             }
12042             
12043             if(this.labellg > 0){
12044                 labelCfg.cls += ' col-lg-' + this.labellg;
12045                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12046             }
12047             
12048             if(this.labelmd > 0){
12049                 labelCfg.cls += ' col-md-' + this.labelmd;
12050                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12051             }
12052             
12053             if(this.labelsm > 0){
12054                 labelCfg.cls += ' col-sm-' + this.labelsm;
12055                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12056             }
12057             
12058             if(this.labelxs > 0){
12059                 labelCfg.cls += ' col-xs-' + this.labelxs;
12060                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12061             }
12062             
12063         } else if ( this.fieldLabel.length) {
12064 //                Roo.log(" label");
12065             cfg.cn = [
12066                 indicator,
12067                {
12068                    tag: 'label',
12069                    //cls : 'input-group-addon',
12070                    html : this.fieldLabel
12071
12072                },
12073
12074                combobox
12075
12076             ];
12077             
12078             if(this.indicatorpos == 'right'){
12079                 
12080                 cfg.cn = [
12081                     {
12082                        tag: 'label',
12083                        cn : [
12084                            {
12085                                tag : 'span',
12086                                html : this.fieldLabel
12087                            },
12088                            indicator
12089                        ]
12090
12091                     },
12092                     combobox
12093
12094                 ];
12095
12096             }
12097
12098         } else {
12099             
12100 //                Roo.log(" no label && no align");
12101                 cfg = combobox
12102                      
12103                 
12104         }
12105         
12106         var settings=this;
12107         ['xs','sm','md','lg'].map(function(size){
12108             if (settings[size]) {
12109                 cfg.cls += ' col-' + size + '-' + settings[size];
12110             }
12111         });
12112         
12113         return cfg;
12114         
12115     },
12116     
12117     
12118     
12119     // private
12120     onResize : function(w, h){
12121 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12122 //        if(typeof w == 'number'){
12123 //            var x = w - this.trigger.getWidth();
12124 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12125 //            this.trigger.setStyle('left', x+'px');
12126 //        }
12127     },
12128
12129     // private
12130     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12131
12132     // private
12133     getResizeEl : function(){
12134         return this.inputEl();
12135     },
12136
12137     // private
12138     getPositionEl : function(){
12139         return this.inputEl();
12140     },
12141
12142     // private
12143     alignErrorIcon : function(){
12144         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12145     },
12146
12147     // private
12148     initEvents : function(){
12149         
12150         this.createList();
12151         
12152         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12153         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12154         if(!this.multiple && this.showToggleBtn){
12155             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12156             if(this.hideTrigger){
12157                 this.trigger.setDisplayed(false);
12158             }
12159             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12160         }
12161         
12162         if(this.multiple){
12163             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12164         }
12165         
12166         if(this.removable && !this.editable && !this.tickable){
12167             var close = this.closeTriggerEl();
12168             
12169             if(close){
12170                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12171                 close.on('click', this.removeBtnClick, this, close);
12172             }
12173         }
12174         
12175         //this.trigger.addClassOnOver('x-form-trigger-over');
12176         //this.trigger.addClassOnClick('x-form-trigger-click');
12177         
12178         //if(!this.width){
12179         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12180         //}
12181     },
12182     
12183     closeTriggerEl : function()
12184     {
12185         var close = this.el.select('.roo-combo-removable-btn', true).first();
12186         return close ? close : false;
12187     },
12188     
12189     removeBtnClick : function(e, h, el)
12190     {
12191         e.preventDefault();
12192         
12193         if(this.fireEvent("remove", this) !== false){
12194             this.reset();
12195             this.fireEvent("afterremove", this)
12196         }
12197     },
12198     
12199     createList : function()
12200     {
12201         this.list = Roo.get(document.body).createChild({
12202             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12203             cls: 'typeahead typeahead-long dropdown-menu',
12204             style: 'display:none'
12205         });
12206         
12207         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12208         
12209     },
12210
12211     // private
12212     initTrigger : function(){
12213        
12214     },
12215
12216     // private
12217     onDestroy : function(){
12218         if(this.trigger){
12219             this.trigger.removeAllListeners();
12220           //  this.trigger.remove();
12221         }
12222         //if(this.wrap){
12223         //    this.wrap.remove();
12224         //}
12225         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12226     },
12227
12228     // private
12229     onFocus : function(){
12230         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12231         /*
12232         if(!this.mimicing){
12233             this.wrap.addClass('x-trigger-wrap-focus');
12234             this.mimicing = true;
12235             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12236             if(this.monitorTab){
12237                 this.el.on("keydown", this.checkTab, this);
12238             }
12239         }
12240         */
12241     },
12242
12243     // private
12244     checkTab : function(e){
12245         if(e.getKey() == e.TAB){
12246             this.triggerBlur();
12247         }
12248     },
12249
12250     // private
12251     onBlur : function(){
12252         // do nothing
12253     },
12254
12255     // private
12256     mimicBlur : function(e, t){
12257         /*
12258         if(!this.wrap.contains(t) && this.validateBlur()){
12259             this.triggerBlur();
12260         }
12261         */
12262     },
12263
12264     // private
12265     triggerBlur : function(){
12266         this.mimicing = false;
12267         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12268         if(this.monitorTab){
12269             this.el.un("keydown", this.checkTab, this);
12270         }
12271         //this.wrap.removeClass('x-trigger-wrap-focus');
12272         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12273     },
12274
12275     // private
12276     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12277     validateBlur : function(e, t){
12278         return true;
12279     },
12280
12281     // private
12282     onDisable : function(){
12283         this.inputEl().dom.disabled = true;
12284         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12285         //if(this.wrap){
12286         //    this.wrap.addClass('x-item-disabled');
12287         //}
12288     },
12289
12290     // private
12291     onEnable : function(){
12292         this.inputEl().dom.disabled = false;
12293         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12294         //if(this.wrap){
12295         //    this.el.removeClass('x-item-disabled');
12296         //}
12297     },
12298
12299     // private
12300     onShow : function(){
12301         var ae = this.getActionEl();
12302         
12303         if(ae){
12304             ae.dom.style.display = '';
12305             ae.dom.style.visibility = 'visible';
12306         }
12307     },
12308
12309     // private
12310     
12311     onHide : function(){
12312         var ae = this.getActionEl();
12313         ae.dom.style.display = 'none';
12314     },
12315
12316     /**
12317      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12318      * by an implementing function.
12319      * @method
12320      * @param {EventObject} e
12321      */
12322     onTriggerClick : Roo.emptyFn
12323 });
12324  
12325 /*
12326 * Licence: LGPL
12327 */
12328
12329 /**
12330  * @class Roo.bootstrap.CardUploader
12331  * @extends Roo.bootstrap.Button
12332  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12333  * @cfg {Number} errorTimeout default 3000
12334  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12335  * @cfg {Array}  html The button text.
12336
12337  *
12338  * @constructor
12339  * Create a new CardUploader
12340  * @param {Object} config The config object
12341  */
12342
12343 Roo.bootstrap.CardUploader = function(config){
12344     
12345  
12346     
12347     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12348     
12349     
12350     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12351         return r.data.id
12352         });
12353     
12354     
12355 };
12356
12357 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12358     
12359      
12360     errorTimeout : 3000,
12361      
12362     images : false,
12363    
12364     fileCollection : false,
12365     allowBlank : true,
12366     
12367     getAutoCreate : function()
12368     {
12369         
12370         var cfg =  {
12371             cls :'form-group' ,
12372             cn : [
12373                
12374                 {
12375                     tag: 'label',
12376                    //cls : 'input-group-addon',
12377                     html : this.fieldLabel
12378
12379                 },
12380
12381                 {
12382                     tag: 'input',
12383                     type : 'hidden',
12384                     value : this.value,
12385                     cls : 'd-none  form-control'
12386                 },
12387                 
12388                 {
12389                     tag: 'input',
12390                     multiple : 'multiple',
12391                     type : 'file',
12392                     cls : 'd-none  roo-card-upload-selector'
12393                 },
12394                 
12395                 {
12396                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12397                 },
12398                 {
12399                     cls : 'card-columns roo-card-uploader-container'
12400                 }
12401
12402             ]
12403         };
12404            
12405          
12406         return cfg;
12407     },
12408     
12409     getChildContainer : function() /// what children are added to.
12410     {
12411         return this.containerEl;
12412     },
12413    
12414     getButtonContainer : function() /// what children are added to.
12415     {
12416         return this.el.select(".roo-card-uploader-button-container").first();
12417     },
12418    
12419     initEvents : function()
12420     {
12421         
12422         Roo.bootstrap.Input.prototype.initEvents.call(this);
12423         
12424         var t = this;
12425         this.addxtype({
12426             xns: Roo.bootstrap,
12427
12428             xtype : 'Button',
12429             container_method : 'getButtonContainer' ,            
12430             html :  this.html, // fix changable?
12431             cls : 'w-100 ',
12432             listeners : {
12433                 'click' : function(btn, e) {
12434                     t.onClick(e);
12435                 }
12436             }
12437         });
12438         
12439         
12440         
12441         
12442         this.urlAPI = (window.createObjectURL && window) || 
12443                                 (window.URL && URL.revokeObjectURL && URL) || 
12444                                 (window.webkitURL && webkitURL);
12445                         
12446          
12447          
12448          
12449         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12450         
12451         this.selectorEl.on('change', this.onFileSelected, this);
12452         if (this.images) {
12453             var t = this;
12454             this.images.forEach(function(img) {
12455                 t.addCard(img)
12456             });
12457             this.images = false;
12458         }
12459         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12460          
12461        
12462     },
12463     
12464    
12465     onClick : function(e)
12466     {
12467         e.preventDefault();
12468          
12469         this.selectorEl.dom.click();
12470          
12471     },
12472     
12473     onFileSelected : function(e)
12474     {
12475         e.preventDefault();
12476         
12477         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12478             return;
12479         }
12480         
12481         Roo.each(this.selectorEl.dom.files, function(file){    
12482             this.addFile(file);
12483         }, this);
12484          
12485     },
12486     
12487       
12488     
12489       
12490     
12491     addFile : function(file)
12492     {
12493            
12494         if(typeof(file) === 'string'){
12495             throw "Add file by name?"; // should not happen
12496             return;
12497         }
12498         
12499         if(!file || !this.urlAPI){
12500             return;
12501         }
12502         
12503         // file;
12504         // file.type;
12505         
12506         var _this = this;
12507         
12508         
12509         var url = _this.urlAPI.createObjectURL( file);
12510            
12511         this.addCard({
12512             id : Roo.bootstrap.CardUploader.ID--,
12513             is_uploaded : false,
12514             src : url,
12515             title : file.name,
12516             mimetype : file.type,
12517             preview : false,
12518             is_deleted : 0
12519         })
12520         
12521     },
12522     
12523     addCard : function (data)
12524     {
12525         // hidden input element?
12526         // if the file is not an image...
12527         //then we need to use something other that and header_image
12528         var t = this;
12529         //   remove.....
12530         var footer = [
12531             {
12532                 xns : Roo.bootstrap,
12533                 xtype : 'CardFooter',
12534                 items: [
12535                     {
12536                         xns : Roo.bootstrap,
12537                         xtype : 'Element',
12538                         cls : 'd-flex',
12539                         items : [
12540                             
12541                             {
12542                                 xns : Roo.bootstrap,
12543                                 xtype : 'Button',
12544                                 html : String.format("<small>{0}</small>", data.title),
12545                                 cls : 'col-11 text-left',
12546                                 size: 'sm',
12547                                 weight: 'link',
12548                                 fa : 'download',
12549                                 listeners : {
12550                                     click : function() {
12551                                         this.downloadCard(data.id)
12552                                     }
12553                                 }
12554                             },
12555                           
12556                             {
12557                                 xns : Roo.bootstrap,
12558                                 xtype : 'Button',
12559                                 
12560                                 size : 'sm',
12561                                 weight: 'danger',
12562                                 cls : 'col-1',
12563                                 fa : 'times',
12564                                 listeners : {
12565                                     click : function() {
12566                                         t.removeCard(data.id)
12567                                     }
12568                                 }
12569                             }
12570                         ]
12571                     }
12572                     
12573                 ] 
12574             }
12575             
12576         ];
12577
12578         var cn = this.addxtype(
12579             {
12580                  
12581                 xns : Roo.bootstrap,
12582                 xtype : 'Card',
12583                 closeable : true,
12584                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12585                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12586                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12587                 data : data,
12588                 html : false,
12589                  
12590                 items : footer,
12591                 initEvents : function() {
12592                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12593                     this.imgEl = this.el.select('.card-img-top').first();
12594                     if (this.imgEl) {
12595                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12596                         this.imgEl.set({ 'pointer' : 'cursor' });
12597                                   
12598                     }
12599                     
12600                   
12601                 }
12602                 
12603             }
12604         );
12605         // dont' really need ot update items.
12606         // this.items.push(cn);
12607         this.fileCollection.add(cn);
12608         this.updateInput();
12609         
12610     },
12611     removeCard : function(id)
12612     {
12613         
12614         var card  = this.fileCollection.get(id);
12615         card.data.is_deleted = 1;
12616         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12617         this.fileCollection.remove(card);
12618         //this.items = this.items.filter(function(e) { return e != card });
12619         // dont' really need ot update items.
12620         card.el.dom.parentNode.removeChild(card.el.dom);
12621         
12622     },
12623     reset: function()
12624     {
12625         this.fileCollection.each(function(card) {
12626             card.el.dom.parentNode.removeChild(card.el.dom);    
12627         });
12628         this.fileCollection.clear();
12629         this.updateInput();
12630     },
12631     
12632     updateInput : function()
12633     {
12634         var data = [];
12635         this.fileCollection.each(function(e) {
12636             data.push(e.data);
12637         });
12638         
12639         this.inputEl().dom.value = JSON.stringify(data);
12640     }
12641     
12642     
12643 });
12644
12645
12646 Roo.bootstrap.CardUploader.ID = -1;/*
12647  * Based on:
12648  * Ext JS Library 1.1.1
12649  * Copyright(c) 2006-2007, Ext JS, LLC.
12650  *
12651  * Originally Released Under LGPL - original licence link has changed is not relivant.
12652  *
12653  * Fork - LGPL
12654  * <script type="text/javascript">
12655  */
12656
12657
12658 /**
12659  * @class Roo.data.SortTypes
12660  * @singleton
12661  * Defines the default sorting (casting?) comparison functions used when sorting data.
12662  */
12663 Roo.data.SortTypes = {
12664     /**
12665      * Default sort that does nothing
12666      * @param {Mixed} s The value being converted
12667      * @return {Mixed} The comparison value
12668      */
12669     none : function(s){
12670         return s;
12671     },
12672     
12673     /**
12674      * The regular expression used to strip tags
12675      * @type {RegExp}
12676      * @property
12677      */
12678     stripTagsRE : /<\/?[^>]+>/gi,
12679     
12680     /**
12681      * Strips all HTML tags to sort on text only
12682      * @param {Mixed} s The value being converted
12683      * @return {String} The comparison value
12684      */
12685     asText : function(s){
12686         return String(s).replace(this.stripTagsRE, "");
12687     },
12688     
12689     /**
12690      * Strips all HTML tags to sort on text only - Case insensitive
12691      * @param {Mixed} s The value being converted
12692      * @return {String} The comparison value
12693      */
12694     asUCText : function(s){
12695         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12696     },
12697     
12698     /**
12699      * Case insensitive string
12700      * @param {Mixed} s The value being converted
12701      * @return {String} The comparison value
12702      */
12703     asUCString : function(s) {
12704         return String(s).toUpperCase();
12705     },
12706     
12707     /**
12708      * Date sorting
12709      * @param {Mixed} s The value being converted
12710      * @return {Number} The comparison value
12711      */
12712     asDate : function(s) {
12713         if(!s){
12714             return 0;
12715         }
12716         if(s instanceof Date){
12717             return s.getTime();
12718         }
12719         return Date.parse(String(s));
12720     },
12721     
12722     /**
12723      * Float sorting
12724      * @param {Mixed} s The value being converted
12725      * @return {Float} The comparison value
12726      */
12727     asFloat : function(s) {
12728         var val = parseFloat(String(s).replace(/,/g, ""));
12729         if(isNaN(val)) {
12730             val = 0;
12731         }
12732         return val;
12733     },
12734     
12735     /**
12736      * Integer sorting
12737      * @param {Mixed} s The value being converted
12738      * @return {Number} The comparison value
12739      */
12740     asInt : function(s) {
12741         var val = parseInt(String(s).replace(/,/g, ""));
12742         if(isNaN(val)) {
12743             val = 0;
12744         }
12745         return val;
12746     }
12747 };/*
12748  * Based on:
12749  * Ext JS Library 1.1.1
12750  * Copyright(c) 2006-2007, Ext JS, LLC.
12751  *
12752  * Originally Released Under LGPL - original licence link has changed is not relivant.
12753  *
12754  * Fork - LGPL
12755  * <script type="text/javascript">
12756  */
12757
12758 /**
12759 * @class Roo.data.Record
12760  * Instances of this class encapsulate both record <em>definition</em> information, and record
12761  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12762  * to access Records cached in an {@link Roo.data.Store} object.<br>
12763  * <p>
12764  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12765  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12766  * objects.<br>
12767  * <p>
12768  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12769  * @constructor
12770  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12771  * {@link #create}. The parameters are the same.
12772  * @param {Array} data An associative Array of data values keyed by the field name.
12773  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12774  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12775  * not specified an integer id is generated.
12776  */
12777 Roo.data.Record = function(data, id){
12778     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12779     this.data = data;
12780 };
12781
12782 /**
12783  * Generate a constructor for a specific record layout.
12784  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12785  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12786  * Each field definition object may contain the following properties: <ul>
12787  * <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,
12788  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12789  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12790  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12791  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12792  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12793  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12794  * this may be omitted.</p></li>
12795  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12796  * <ul><li>auto (Default, implies no conversion)</li>
12797  * <li>string</li>
12798  * <li>int</li>
12799  * <li>float</li>
12800  * <li>boolean</li>
12801  * <li>date</li></ul></p></li>
12802  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12803  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12804  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12805  * by the Reader into an object that will be stored in the Record. It is passed the
12806  * following parameters:<ul>
12807  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12808  * </ul></p></li>
12809  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12810  * </ul>
12811  * <br>usage:<br><pre><code>
12812 var TopicRecord = Roo.data.Record.create(
12813     {name: 'title', mapping: 'topic_title'},
12814     {name: 'author', mapping: 'username'},
12815     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12816     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12817     {name: 'lastPoster', mapping: 'user2'},
12818     {name: 'excerpt', mapping: 'post_text'}
12819 );
12820
12821 var myNewRecord = new TopicRecord({
12822     title: 'Do my job please',
12823     author: 'noobie',
12824     totalPosts: 1,
12825     lastPost: new Date(),
12826     lastPoster: 'Animal',
12827     excerpt: 'No way dude!'
12828 });
12829 myStore.add(myNewRecord);
12830 </code></pre>
12831  * @method create
12832  * @static
12833  */
12834 Roo.data.Record.create = function(o){
12835     var f = function(){
12836         f.superclass.constructor.apply(this, arguments);
12837     };
12838     Roo.extend(f, Roo.data.Record);
12839     var p = f.prototype;
12840     p.fields = new Roo.util.MixedCollection(false, function(field){
12841         return field.name;
12842     });
12843     for(var i = 0, len = o.length; i < len; i++){
12844         p.fields.add(new Roo.data.Field(o[i]));
12845     }
12846     f.getField = function(name){
12847         return p.fields.get(name);  
12848     };
12849     return f;
12850 };
12851
12852 Roo.data.Record.AUTO_ID = 1000;
12853 Roo.data.Record.EDIT = 'edit';
12854 Roo.data.Record.REJECT = 'reject';
12855 Roo.data.Record.COMMIT = 'commit';
12856
12857 Roo.data.Record.prototype = {
12858     /**
12859      * Readonly flag - true if this record has been modified.
12860      * @type Boolean
12861      */
12862     dirty : false,
12863     editing : false,
12864     error: null,
12865     modified: null,
12866
12867     // private
12868     join : function(store){
12869         this.store = store;
12870     },
12871
12872     /**
12873      * Set the named field to the specified value.
12874      * @param {String} name The name of the field to set.
12875      * @param {Object} value The value to set the field to.
12876      */
12877     set : function(name, value){
12878         if(this.data[name] == value){
12879             return;
12880         }
12881         this.dirty = true;
12882         if(!this.modified){
12883             this.modified = {};
12884         }
12885         if(typeof this.modified[name] == 'undefined'){
12886             this.modified[name] = this.data[name];
12887         }
12888         this.data[name] = value;
12889         if(!this.editing && this.store){
12890             this.store.afterEdit(this);
12891         }       
12892     },
12893
12894     /**
12895      * Get the value of the named field.
12896      * @param {String} name The name of the field to get the value of.
12897      * @return {Object} The value of the field.
12898      */
12899     get : function(name){
12900         return this.data[name]; 
12901     },
12902
12903     // private
12904     beginEdit : function(){
12905         this.editing = true;
12906         this.modified = {}; 
12907     },
12908
12909     // private
12910     cancelEdit : function(){
12911         this.editing = false;
12912         delete this.modified;
12913     },
12914
12915     // private
12916     endEdit : function(){
12917         this.editing = false;
12918         if(this.dirty && this.store){
12919             this.store.afterEdit(this);
12920         }
12921     },
12922
12923     /**
12924      * Usually called by the {@link Roo.data.Store} which owns the Record.
12925      * Rejects all changes made to the Record since either creation, or the last commit operation.
12926      * Modified fields are reverted to their original values.
12927      * <p>
12928      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12929      * of reject operations.
12930      */
12931     reject : function(){
12932         var m = this.modified;
12933         for(var n in m){
12934             if(typeof m[n] != "function"){
12935                 this.data[n] = m[n];
12936             }
12937         }
12938         this.dirty = false;
12939         delete this.modified;
12940         this.editing = false;
12941         if(this.store){
12942             this.store.afterReject(this);
12943         }
12944     },
12945
12946     /**
12947      * Usually called by the {@link Roo.data.Store} which owns the Record.
12948      * Commits all changes made to the Record since either creation, or the last commit operation.
12949      * <p>
12950      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12951      * of commit operations.
12952      */
12953     commit : function(){
12954         this.dirty = false;
12955         delete this.modified;
12956         this.editing = false;
12957         if(this.store){
12958             this.store.afterCommit(this);
12959         }
12960     },
12961
12962     // private
12963     hasError : function(){
12964         return this.error != null;
12965     },
12966
12967     // private
12968     clearError : function(){
12969         this.error = null;
12970     },
12971
12972     /**
12973      * Creates a copy of this record.
12974      * @param {String} id (optional) A new record id if you don't want to use this record's id
12975      * @return {Record}
12976      */
12977     copy : function(newId) {
12978         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12979     }
12980 };/*
12981  * Based on:
12982  * Ext JS Library 1.1.1
12983  * Copyright(c) 2006-2007, Ext JS, LLC.
12984  *
12985  * Originally Released Under LGPL - original licence link has changed is not relivant.
12986  *
12987  * Fork - LGPL
12988  * <script type="text/javascript">
12989  */
12990
12991
12992
12993 /**
12994  * @class Roo.data.Store
12995  * @extends Roo.util.Observable
12996  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12997  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12998  * <p>
12999  * 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
13000  * has no knowledge of the format of the data returned by the Proxy.<br>
13001  * <p>
13002  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13003  * instances from the data object. These records are cached and made available through accessor functions.
13004  * @constructor
13005  * Creates a new Store.
13006  * @param {Object} config A config object containing the objects needed for the Store to access data,
13007  * and read the data into Records.
13008  */
13009 Roo.data.Store = function(config){
13010     this.data = new Roo.util.MixedCollection(false);
13011     this.data.getKey = function(o){
13012         return o.id;
13013     };
13014     this.baseParams = {};
13015     // private
13016     this.paramNames = {
13017         "start" : "start",
13018         "limit" : "limit",
13019         "sort" : "sort",
13020         "dir" : "dir",
13021         "multisort" : "_multisort"
13022     };
13023
13024     if(config && config.data){
13025         this.inlineData = config.data;
13026         delete config.data;
13027     }
13028
13029     Roo.apply(this, config);
13030     
13031     if(this.reader){ // reader passed
13032         this.reader = Roo.factory(this.reader, Roo.data);
13033         this.reader.xmodule = this.xmodule || false;
13034         if(!this.recordType){
13035             this.recordType = this.reader.recordType;
13036         }
13037         if(this.reader.onMetaChange){
13038             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13039         }
13040     }
13041
13042     if(this.recordType){
13043         this.fields = this.recordType.prototype.fields;
13044     }
13045     this.modified = [];
13046
13047     this.addEvents({
13048         /**
13049          * @event datachanged
13050          * Fires when the data cache has changed, and a widget which is using this Store
13051          * as a Record cache should refresh its view.
13052          * @param {Store} this
13053          */
13054         datachanged : true,
13055         /**
13056          * @event metachange
13057          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13058          * @param {Store} this
13059          * @param {Object} meta The JSON metadata
13060          */
13061         metachange : true,
13062         /**
13063          * @event add
13064          * Fires when Records have been added to the Store
13065          * @param {Store} this
13066          * @param {Roo.data.Record[]} records The array of Records added
13067          * @param {Number} index The index at which the record(s) were added
13068          */
13069         add : true,
13070         /**
13071          * @event remove
13072          * Fires when a Record has been removed from the Store
13073          * @param {Store} this
13074          * @param {Roo.data.Record} record The Record that was removed
13075          * @param {Number} index The index at which the record was removed
13076          */
13077         remove : true,
13078         /**
13079          * @event update
13080          * Fires when a Record has been updated
13081          * @param {Store} this
13082          * @param {Roo.data.Record} record The Record that was updated
13083          * @param {String} operation The update operation being performed.  Value may be one of:
13084          * <pre><code>
13085  Roo.data.Record.EDIT
13086  Roo.data.Record.REJECT
13087  Roo.data.Record.COMMIT
13088          * </code></pre>
13089          */
13090         update : true,
13091         /**
13092          * @event clear
13093          * Fires when the data cache has been cleared.
13094          * @param {Store} this
13095          */
13096         clear : true,
13097         /**
13098          * @event beforeload
13099          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13100          * the load action will be canceled.
13101          * @param {Store} this
13102          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13103          */
13104         beforeload : true,
13105         /**
13106          * @event beforeloadadd
13107          * Fires after a new set of Records has been loaded.
13108          * @param {Store} this
13109          * @param {Roo.data.Record[]} records The Records that were loaded
13110          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13111          */
13112         beforeloadadd : true,
13113         /**
13114          * @event load
13115          * Fires after a new set of Records has been loaded, before they are added to the store.
13116          * @param {Store} this
13117          * @param {Roo.data.Record[]} records The Records that were loaded
13118          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13119          * @params {Object} return from reader
13120          */
13121         load : true,
13122         /**
13123          * @event loadexception
13124          * Fires if an exception occurs in the Proxy during loading.
13125          * Called with the signature of the Proxy's "loadexception" event.
13126          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13127          * 
13128          * @param {Proxy} 
13129          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13130          * @param {Object} load options 
13131          * @param {Object} jsonData from your request (normally this contains the Exception)
13132          */
13133         loadexception : true
13134     });
13135     
13136     if(this.proxy){
13137         this.proxy = Roo.factory(this.proxy, Roo.data);
13138         this.proxy.xmodule = this.xmodule || false;
13139         this.relayEvents(this.proxy,  ["loadexception"]);
13140     }
13141     this.sortToggle = {};
13142     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13143
13144     Roo.data.Store.superclass.constructor.call(this);
13145
13146     if(this.inlineData){
13147         this.loadData(this.inlineData);
13148         delete this.inlineData;
13149     }
13150 };
13151
13152 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13153      /**
13154     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13155     * without a remote query - used by combo/forms at present.
13156     */
13157     
13158     /**
13159     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13160     */
13161     /**
13162     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13163     */
13164     /**
13165     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13166     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13167     */
13168     /**
13169     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13170     * on any HTTP request
13171     */
13172     /**
13173     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13174     */
13175     /**
13176     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13177     */
13178     multiSort: false,
13179     /**
13180     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13181     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13182     */
13183     remoteSort : false,
13184
13185     /**
13186     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13187      * loaded or when a record is removed. (defaults to false).
13188     */
13189     pruneModifiedRecords : false,
13190
13191     // private
13192     lastOptions : null,
13193
13194     /**
13195      * Add Records to the Store and fires the add event.
13196      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13197      */
13198     add : function(records){
13199         records = [].concat(records);
13200         for(var i = 0, len = records.length; i < len; i++){
13201             records[i].join(this);
13202         }
13203         var index = this.data.length;
13204         this.data.addAll(records);
13205         this.fireEvent("add", this, records, index);
13206     },
13207
13208     /**
13209      * Remove a Record from the Store and fires the remove event.
13210      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13211      */
13212     remove : function(record){
13213         var index = this.data.indexOf(record);
13214         this.data.removeAt(index);
13215  
13216         if(this.pruneModifiedRecords){
13217             this.modified.remove(record);
13218         }
13219         this.fireEvent("remove", this, record, index);
13220     },
13221
13222     /**
13223      * Remove all Records from the Store and fires the clear event.
13224      */
13225     removeAll : function(){
13226         this.data.clear();
13227         if(this.pruneModifiedRecords){
13228             this.modified = [];
13229         }
13230         this.fireEvent("clear", this);
13231     },
13232
13233     /**
13234      * Inserts Records to the Store at the given index and fires the add event.
13235      * @param {Number} index The start index at which to insert the passed Records.
13236      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13237      */
13238     insert : function(index, records){
13239         records = [].concat(records);
13240         for(var i = 0, len = records.length; i < len; i++){
13241             this.data.insert(index, records[i]);
13242             records[i].join(this);
13243         }
13244         this.fireEvent("add", this, records, index);
13245     },
13246
13247     /**
13248      * Get the index within the cache of the passed Record.
13249      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13250      * @return {Number} The index of the passed Record. Returns -1 if not found.
13251      */
13252     indexOf : function(record){
13253         return this.data.indexOf(record);
13254     },
13255
13256     /**
13257      * Get the index within the cache of the Record with the passed id.
13258      * @param {String} id The id of the Record to find.
13259      * @return {Number} The index of the Record. Returns -1 if not found.
13260      */
13261     indexOfId : function(id){
13262         return this.data.indexOfKey(id);
13263     },
13264
13265     /**
13266      * Get the Record with the specified id.
13267      * @param {String} id The id of the Record to find.
13268      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13269      */
13270     getById : function(id){
13271         return this.data.key(id);
13272     },
13273
13274     /**
13275      * Get the Record at the specified index.
13276      * @param {Number} index The index of the Record to find.
13277      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13278      */
13279     getAt : function(index){
13280         return this.data.itemAt(index);
13281     },
13282
13283     /**
13284      * Returns a range of Records between specified indices.
13285      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13286      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13287      * @return {Roo.data.Record[]} An array of Records
13288      */
13289     getRange : function(start, end){
13290         return this.data.getRange(start, end);
13291     },
13292
13293     // private
13294     storeOptions : function(o){
13295         o = Roo.apply({}, o);
13296         delete o.callback;
13297         delete o.scope;
13298         this.lastOptions = o;
13299     },
13300
13301     /**
13302      * Loads the Record cache from the configured Proxy using the configured Reader.
13303      * <p>
13304      * If using remote paging, then the first load call must specify the <em>start</em>
13305      * and <em>limit</em> properties in the options.params property to establish the initial
13306      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13307      * <p>
13308      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13309      * and this call will return before the new data has been loaded. Perform any post-processing
13310      * in a callback function, or in a "load" event handler.</strong>
13311      * <p>
13312      * @param {Object} options An object containing properties which control loading options:<ul>
13313      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13314      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13315      * passed the following arguments:<ul>
13316      * <li>r : Roo.data.Record[]</li>
13317      * <li>options: Options object from the load call</li>
13318      * <li>success: Boolean success indicator</li></ul></li>
13319      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13320      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13321      * </ul>
13322      */
13323     load : function(options){
13324         options = options || {};
13325         if(this.fireEvent("beforeload", this, options) !== false){
13326             this.storeOptions(options);
13327             var p = Roo.apply(options.params || {}, this.baseParams);
13328             // if meta was not loaded from remote source.. try requesting it.
13329             if (!this.reader.metaFromRemote) {
13330                 p._requestMeta = 1;
13331             }
13332             if(this.sortInfo && this.remoteSort){
13333                 var pn = this.paramNames;
13334                 p[pn["sort"]] = this.sortInfo.field;
13335                 p[pn["dir"]] = this.sortInfo.direction;
13336             }
13337             if (this.multiSort) {
13338                 var pn = this.paramNames;
13339                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13340             }
13341             
13342             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13343         }
13344     },
13345
13346     /**
13347      * Reloads the Record cache from the configured Proxy using the configured Reader and
13348      * the options from the last load operation performed.
13349      * @param {Object} options (optional) An object containing properties which may override the options
13350      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13351      * the most recently used options are reused).
13352      */
13353     reload : function(options){
13354         this.load(Roo.applyIf(options||{}, this.lastOptions));
13355     },
13356
13357     // private
13358     // Called as a callback by the Reader during a load operation.
13359     loadRecords : function(o, options, success){
13360         if(!o || success === false){
13361             if(success !== false){
13362                 this.fireEvent("load", this, [], options, o);
13363             }
13364             if(options.callback){
13365                 options.callback.call(options.scope || this, [], options, false);
13366             }
13367             return;
13368         }
13369         // if data returned failure - throw an exception.
13370         if (o.success === false) {
13371             // show a message if no listener is registered.
13372             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13373                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13374             }
13375             // loadmask wil be hooked into this..
13376             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13377             return;
13378         }
13379         var r = o.records, t = o.totalRecords || r.length;
13380         
13381         this.fireEvent("beforeloadadd", this, r, options, o);
13382         
13383         if(!options || options.add !== true){
13384             if(this.pruneModifiedRecords){
13385                 this.modified = [];
13386             }
13387             for(var i = 0, len = r.length; i < len; i++){
13388                 r[i].join(this);
13389             }
13390             if(this.snapshot){
13391                 this.data = this.snapshot;
13392                 delete this.snapshot;
13393             }
13394             this.data.clear();
13395             this.data.addAll(r);
13396             this.totalLength = t;
13397             this.applySort();
13398             this.fireEvent("datachanged", this);
13399         }else{
13400             this.totalLength = Math.max(t, this.data.length+r.length);
13401             this.add(r);
13402         }
13403         
13404         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13405                 
13406             var e = new Roo.data.Record({});
13407
13408             e.set(this.parent.displayField, this.parent.emptyTitle);
13409             e.set(this.parent.valueField, '');
13410
13411             this.insert(0, e);
13412         }
13413             
13414         this.fireEvent("load", this, r, options, o);
13415         if(options.callback){
13416             options.callback.call(options.scope || this, r, options, true);
13417         }
13418     },
13419
13420
13421     /**
13422      * Loads data from a passed data block. A Reader which understands the format of the data
13423      * must have been configured in the constructor.
13424      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13425      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13426      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13427      */
13428     loadData : function(o, append){
13429         var r = this.reader.readRecords(o);
13430         this.loadRecords(r, {add: append}, true);
13431     },
13432     
13433      /**
13434      * using 'cn' the nested child reader read the child array into it's child stores.
13435      * @param {Object} rec The record with a 'children array
13436      */
13437     loadDataFromChildren : function(rec)
13438     {
13439         this.loadData(this.reader.toLoadData(rec));
13440     },
13441     
13442
13443     /**
13444      * Gets the number of cached records.
13445      * <p>
13446      * <em>If using paging, this may not be the total size of the dataset. If the data object
13447      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13448      * the data set size</em>
13449      */
13450     getCount : function(){
13451         return this.data.length || 0;
13452     },
13453
13454     /**
13455      * Gets the total number of records in the dataset as returned by the server.
13456      * <p>
13457      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13458      * the dataset size</em>
13459      */
13460     getTotalCount : function(){
13461         return this.totalLength || 0;
13462     },
13463
13464     /**
13465      * Returns the sort state of the Store as an object with two properties:
13466      * <pre><code>
13467  field {String} The name of the field by which the Records are sorted
13468  direction {String} The sort order, "ASC" or "DESC"
13469      * </code></pre>
13470      */
13471     getSortState : function(){
13472         return this.sortInfo;
13473     },
13474
13475     // private
13476     applySort : function(){
13477         if(this.sortInfo && !this.remoteSort){
13478             var s = this.sortInfo, f = s.field;
13479             var st = this.fields.get(f).sortType;
13480             var fn = function(r1, r2){
13481                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13482                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13483             };
13484             this.data.sort(s.direction, fn);
13485             if(this.snapshot && this.snapshot != this.data){
13486                 this.snapshot.sort(s.direction, fn);
13487             }
13488         }
13489     },
13490
13491     /**
13492      * Sets the default sort column and order to be used by the next load operation.
13493      * @param {String} fieldName The name of the field to sort by.
13494      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13495      */
13496     setDefaultSort : function(field, dir){
13497         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13498     },
13499
13500     /**
13501      * Sort the Records.
13502      * If remote sorting is used, the sort is performed on the server, and the cache is
13503      * reloaded. If local sorting is used, the cache is sorted internally.
13504      * @param {String} fieldName The name of the field to sort by.
13505      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13506      */
13507     sort : function(fieldName, dir){
13508         var f = this.fields.get(fieldName);
13509         if(!dir){
13510             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13511             
13512             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13513                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13514             }else{
13515                 dir = f.sortDir;
13516             }
13517         }
13518         this.sortToggle[f.name] = dir;
13519         this.sortInfo = {field: f.name, direction: dir};
13520         if(!this.remoteSort){
13521             this.applySort();
13522             this.fireEvent("datachanged", this);
13523         }else{
13524             this.load(this.lastOptions);
13525         }
13526     },
13527
13528     /**
13529      * Calls the specified function for each of the Records in the cache.
13530      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13531      * Returning <em>false</em> aborts and exits the iteration.
13532      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13533      */
13534     each : function(fn, scope){
13535         this.data.each(fn, scope);
13536     },
13537
13538     /**
13539      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13540      * (e.g., during paging).
13541      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13542      */
13543     getModifiedRecords : function(){
13544         return this.modified;
13545     },
13546
13547     // private
13548     createFilterFn : function(property, value, anyMatch){
13549         if(!value.exec){ // not a regex
13550             value = String(value);
13551             if(value.length == 0){
13552                 return false;
13553             }
13554             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13555         }
13556         return function(r){
13557             return value.test(r.data[property]);
13558         };
13559     },
13560
13561     /**
13562      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13563      * @param {String} property A field on your records
13564      * @param {Number} start The record index to start at (defaults to 0)
13565      * @param {Number} end The last record index to include (defaults to length - 1)
13566      * @return {Number} The sum
13567      */
13568     sum : function(property, start, end){
13569         var rs = this.data.items, v = 0;
13570         start = start || 0;
13571         end = (end || end === 0) ? end : rs.length-1;
13572
13573         for(var i = start; i <= end; i++){
13574             v += (rs[i].data[property] || 0);
13575         }
13576         return v;
13577     },
13578
13579     /**
13580      * Filter the records by a specified property.
13581      * @param {String} field A field on your records
13582      * @param {String/RegExp} value Either a string that the field
13583      * should start with or a RegExp to test against the field
13584      * @param {Boolean} anyMatch True to match any part not just the beginning
13585      */
13586     filter : function(property, value, anyMatch){
13587         var fn = this.createFilterFn(property, value, anyMatch);
13588         return fn ? this.filterBy(fn) : this.clearFilter();
13589     },
13590
13591     /**
13592      * Filter by a function. The specified function will be called with each
13593      * record in this data source. If the function returns true the record is included,
13594      * otherwise it is filtered.
13595      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13596      * @param {Object} scope (optional) The scope of the function (defaults to this)
13597      */
13598     filterBy : function(fn, scope){
13599         this.snapshot = this.snapshot || this.data;
13600         this.data = this.queryBy(fn, scope||this);
13601         this.fireEvent("datachanged", this);
13602     },
13603
13604     /**
13605      * Query the records by a specified property.
13606      * @param {String} field A field on your records
13607      * @param {String/RegExp} value Either a string that the field
13608      * should start with or a RegExp to test against the field
13609      * @param {Boolean} anyMatch True to match any part not just the beginning
13610      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13611      */
13612     query : function(property, value, anyMatch){
13613         var fn = this.createFilterFn(property, value, anyMatch);
13614         return fn ? this.queryBy(fn) : this.data.clone();
13615     },
13616
13617     /**
13618      * Query by a function. The specified function will be called with each
13619      * record in this data source. If the function returns true the record is included
13620      * in the results.
13621      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13622      * @param {Object} scope (optional) The scope of the function (defaults to this)
13623       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13624      **/
13625     queryBy : function(fn, scope){
13626         var data = this.snapshot || this.data;
13627         return data.filterBy(fn, scope||this);
13628     },
13629
13630     /**
13631      * Collects unique values for a particular dataIndex from this store.
13632      * @param {String} dataIndex The property to collect
13633      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13634      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13635      * @return {Array} An array of the unique values
13636      **/
13637     collect : function(dataIndex, allowNull, bypassFilter){
13638         var d = (bypassFilter === true && this.snapshot) ?
13639                 this.snapshot.items : this.data.items;
13640         var v, sv, r = [], l = {};
13641         for(var i = 0, len = d.length; i < len; i++){
13642             v = d[i].data[dataIndex];
13643             sv = String(v);
13644             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13645                 l[sv] = true;
13646                 r[r.length] = v;
13647             }
13648         }
13649         return r;
13650     },
13651
13652     /**
13653      * Revert to a view of the Record cache with no filtering applied.
13654      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13655      */
13656     clearFilter : function(suppressEvent){
13657         if(this.snapshot && this.snapshot != this.data){
13658             this.data = this.snapshot;
13659             delete this.snapshot;
13660             if(suppressEvent !== true){
13661                 this.fireEvent("datachanged", this);
13662             }
13663         }
13664     },
13665
13666     // private
13667     afterEdit : function(record){
13668         if(this.modified.indexOf(record) == -1){
13669             this.modified.push(record);
13670         }
13671         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13672     },
13673     
13674     // private
13675     afterReject : function(record){
13676         this.modified.remove(record);
13677         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13678     },
13679
13680     // private
13681     afterCommit : function(record){
13682         this.modified.remove(record);
13683         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13684     },
13685
13686     /**
13687      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13688      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13689      */
13690     commitChanges : function(){
13691         var m = this.modified.slice(0);
13692         this.modified = [];
13693         for(var i = 0, len = m.length; i < len; i++){
13694             m[i].commit();
13695         }
13696     },
13697
13698     /**
13699      * Cancel outstanding changes on all changed records.
13700      */
13701     rejectChanges : function(){
13702         var m = this.modified.slice(0);
13703         this.modified = [];
13704         for(var i = 0, len = m.length; i < len; i++){
13705             m[i].reject();
13706         }
13707     },
13708
13709     onMetaChange : function(meta, rtype, o){
13710         this.recordType = rtype;
13711         this.fields = rtype.prototype.fields;
13712         delete this.snapshot;
13713         this.sortInfo = meta.sortInfo || this.sortInfo;
13714         this.modified = [];
13715         this.fireEvent('metachange', this, this.reader.meta);
13716     },
13717     
13718     moveIndex : function(data, type)
13719     {
13720         var index = this.indexOf(data);
13721         
13722         var newIndex = index + type;
13723         
13724         this.remove(data);
13725         
13726         this.insert(newIndex, data);
13727         
13728     }
13729 });/*
13730  * Based on:
13731  * Ext JS Library 1.1.1
13732  * Copyright(c) 2006-2007, Ext JS, LLC.
13733  *
13734  * Originally Released Under LGPL - original licence link has changed is not relivant.
13735  *
13736  * Fork - LGPL
13737  * <script type="text/javascript">
13738  */
13739
13740 /**
13741  * @class Roo.data.SimpleStore
13742  * @extends Roo.data.Store
13743  * Small helper class to make creating Stores from Array data easier.
13744  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13745  * @cfg {Array} fields An array of field definition objects, or field name strings.
13746  * @cfg {Object} an existing reader (eg. copied from another store)
13747  * @cfg {Array} data The multi-dimensional array of data
13748  * @constructor
13749  * @param {Object} config
13750  */
13751 Roo.data.SimpleStore = function(config)
13752 {
13753     Roo.data.SimpleStore.superclass.constructor.call(this, {
13754         isLocal : true,
13755         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13756                 id: config.id
13757             },
13758             Roo.data.Record.create(config.fields)
13759         ),
13760         proxy : new Roo.data.MemoryProxy(config.data)
13761     });
13762     this.load();
13763 };
13764 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13765  * Based on:
13766  * Ext JS Library 1.1.1
13767  * Copyright(c) 2006-2007, Ext JS, LLC.
13768  *
13769  * Originally Released Under LGPL - original licence link has changed is not relivant.
13770  *
13771  * Fork - LGPL
13772  * <script type="text/javascript">
13773  */
13774
13775 /**
13776 /**
13777  * @extends Roo.data.Store
13778  * @class Roo.data.JsonStore
13779  * Small helper class to make creating Stores for JSON data easier. <br/>
13780 <pre><code>
13781 var store = new Roo.data.JsonStore({
13782     url: 'get-images.php',
13783     root: 'images',
13784     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13785 });
13786 </code></pre>
13787  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13788  * JsonReader and HttpProxy (unless inline data is provided).</b>
13789  * @cfg {Array} fields An array of field definition objects, or field name strings.
13790  * @constructor
13791  * @param {Object} config
13792  */
13793 Roo.data.JsonStore = function(c){
13794     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13795         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13796         reader: new Roo.data.JsonReader(c, c.fields)
13797     }));
13798 };
13799 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13800  * Based on:
13801  * Ext JS Library 1.1.1
13802  * Copyright(c) 2006-2007, Ext JS, LLC.
13803  *
13804  * Originally Released Under LGPL - original licence link has changed is not relivant.
13805  *
13806  * Fork - LGPL
13807  * <script type="text/javascript">
13808  */
13809
13810  
13811 Roo.data.Field = function(config){
13812     if(typeof config == "string"){
13813         config = {name: config};
13814     }
13815     Roo.apply(this, config);
13816     
13817     if(!this.type){
13818         this.type = "auto";
13819     }
13820     
13821     var st = Roo.data.SortTypes;
13822     // named sortTypes are supported, here we look them up
13823     if(typeof this.sortType == "string"){
13824         this.sortType = st[this.sortType];
13825     }
13826     
13827     // set default sortType for strings and dates
13828     if(!this.sortType){
13829         switch(this.type){
13830             case "string":
13831                 this.sortType = st.asUCString;
13832                 break;
13833             case "date":
13834                 this.sortType = st.asDate;
13835                 break;
13836             default:
13837                 this.sortType = st.none;
13838         }
13839     }
13840
13841     // define once
13842     var stripRe = /[\$,%]/g;
13843
13844     // prebuilt conversion function for this field, instead of
13845     // switching every time we're reading a value
13846     if(!this.convert){
13847         var cv, dateFormat = this.dateFormat;
13848         switch(this.type){
13849             case "":
13850             case "auto":
13851             case undefined:
13852                 cv = function(v){ return v; };
13853                 break;
13854             case "string":
13855                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13856                 break;
13857             case "int":
13858                 cv = function(v){
13859                     return v !== undefined && v !== null && v !== '' ?
13860                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13861                     };
13862                 break;
13863             case "float":
13864                 cv = function(v){
13865                     return v !== undefined && v !== null && v !== '' ?
13866                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
13867                     };
13868                 break;
13869             case "bool":
13870             case "boolean":
13871                 cv = function(v){ return v === true || v === "true" || v == 1; };
13872                 break;
13873             case "date":
13874                 cv = function(v){
13875                     if(!v){
13876                         return '';
13877                     }
13878                     if(v instanceof Date){
13879                         return v;
13880                     }
13881                     if(dateFormat){
13882                         if(dateFormat == "timestamp"){
13883                             return new Date(v*1000);
13884                         }
13885                         return Date.parseDate(v, dateFormat);
13886                     }
13887                     var parsed = Date.parse(v);
13888                     return parsed ? new Date(parsed) : null;
13889                 };
13890              break;
13891             
13892         }
13893         this.convert = cv;
13894     }
13895 };
13896
13897 Roo.data.Field.prototype = {
13898     dateFormat: null,
13899     defaultValue: "",
13900     mapping: null,
13901     sortType : null,
13902     sortDir : "ASC"
13903 };/*
13904  * Based on:
13905  * Ext JS Library 1.1.1
13906  * Copyright(c) 2006-2007, Ext JS, LLC.
13907  *
13908  * Originally Released Under LGPL - original licence link has changed is not relivant.
13909  *
13910  * Fork - LGPL
13911  * <script type="text/javascript">
13912  */
13913  
13914 // Base class for reading structured data from a data source.  This class is intended to be
13915 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13916
13917 /**
13918  * @class Roo.data.DataReader
13919  * Base class for reading structured data from a data source.  This class is intended to be
13920  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13921  */
13922
13923 Roo.data.DataReader = function(meta, recordType){
13924     
13925     this.meta = meta;
13926     
13927     this.recordType = recordType instanceof Array ? 
13928         Roo.data.Record.create(recordType) : recordType;
13929 };
13930
13931 Roo.data.DataReader.prototype = {
13932     
13933     
13934     readerType : 'Data',
13935      /**
13936      * Create an empty record
13937      * @param {Object} data (optional) - overlay some values
13938      * @return {Roo.data.Record} record created.
13939      */
13940     newRow :  function(d) {
13941         var da =  {};
13942         this.recordType.prototype.fields.each(function(c) {
13943             switch( c.type) {
13944                 case 'int' : da[c.name] = 0; break;
13945                 case 'date' : da[c.name] = new Date(); break;
13946                 case 'float' : da[c.name] = 0.0; break;
13947                 case 'boolean' : da[c.name] = false; break;
13948                 default : da[c.name] = ""; break;
13949             }
13950             
13951         });
13952         return new this.recordType(Roo.apply(da, d));
13953     }
13954     
13955     
13956 };/*
13957  * Based on:
13958  * Ext JS Library 1.1.1
13959  * Copyright(c) 2006-2007, Ext JS, LLC.
13960  *
13961  * Originally Released Under LGPL - original licence link has changed is not relivant.
13962  *
13963  * Fork - LGPL
13964  * <script type="text/javascript">
13965  */
13966
13967 /**
13968  * @class Roo.data.DataProxy
13969  * @extends Roo.data.Observable
13970  * This class is an abstract base class for implementations which provide retrieval of
13971  * unformatted data objects.<br>
13972  * <p>
13973  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13974  * (of the appropriate type which knows how to parse the data object) to provide a block of
13975  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13976  * <p>
13977  * Custom implementations must implement the load method as described in
13978  * {@link Roo.data.HttpProxy#load}.
13979  */
13980 Roo.data.DataProxy = function(){
13981     this.addEvents({
13982         /**
13983          * @event beforeload
13984          * Fires before a network request is made to retrieve a data object.
13985          * @param {Object} This DataProxy object.
13986          * @param {Object} params The params parameter to the load function.
13987          */
13988         beforeload : true,
13989         /**
13990          * @event load
13991          * Fires before the load method's callback is called.
13992          * @param {Object} This DataProxy object.
13993          * @param {Object} o The data object.
13994          * @param {Object} arg The callback argument object passed to the load function.
13995          */
13996         load : true,
13997         /**
13998          * @event loadexception
13999          * Fires if an Exception occurs during data retrieval.
14000          * @param {Object} This DataProxy object.
14001          * @param {Object} o The data object.
14002          * @param {Object} arg The callback argument object passed to the load function.
14003          * @param {Object} e The Exception.
14004          */
14005         loadexception : true
14006     });
14007     Roo.data.DataProxy.superclass.constructor.call(this);
14008 };
14009
14010 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14011
14012     /**
14013      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14014      */
14015 /*
14016  * Based on:
14017  * Ext JS Library 1.1.1
14018  * Copyright(c) 2006-2007, Ext JS, LLC.
14019  *
14020  * Originally Released Under LGPL - original licence link has changed is not relivant.
14021  *
14022  * Fork - LGPL
14023  * <script type="text/javascript">
14024  */
14025 /**
14026  * @class Roo.data.MemoryProxy
14027  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14028  * to the Reader when its load method is called.
14029  * @constructor
14030  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14031  */
14032 Roo.data.MemoryProxy = function(data){
14033     if (data.data) {
14034         data = data.data;
14035     }
14036     Roo.data.MemoryProxy.superclass.constructor.call(this);
14037     this.data = data;
14038 };
14039
14040 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14041     
14042     /**
14043      * Load data from the requested source (in this case an in-memory
14044      * data object passed to the constructor), read the data object into
14045      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14046      * process that block using the passed callback.
14047      * @param {Object} params This parameter is not used by the MemoryProxy class.
14048      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14049      * object into a block of Roo.data.Records.
14050      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14051      * The function must be passed <ul>
14052      * <li>The Record block object</li>
14053      * <li>The "arg" argument from the load function</li>
14054      * <li>A boolean success indicator</li>
14055      * </ul>
14056      * @param {Object} scope The scope in which to call the callback
14057      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14058      */
14059     load : function(params, reader, callback, scope, arg){
14060         params = params || {};
14061         var result;
14062         try {
14063             result = reader.readRecords(params.data ? params.data :this.data);
14064         }catch(e){
14065             this.fireEvent("loadexception", this, arg, null, e);
14066             callback.call(scope, null, arg, false);
14067             return;
14068         }
14069         callback.call(scope, result, arg, true);
14070     },
14071     
14072     // private
14073     update : function(params, records){
14074         
14075     }
14076 });/*
14077  * Based on:
14078  * Ext JS Library 1.1.1
14079  * Copyright(c) 2006-2007, Ext JS, LLC.
14080  *
14081  * Originally Released Under LGPL - original licence link has changed is not relivant.
14082  *
14083  * Fork - LGPL
14084  * <script type="text/javascript">
14085  */
14086 /**
14087  * @class Roo.data.HttpProxy
14088  * @extends Roo.data.DataProxy
14089  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14090  * configured to reference a certain URL.<br><br>
14091  * <p>
14092  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14093  * from which the running page was served.<br><br>
14094  * <p>
14095  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14096  * <p>
14097  * Be aware that to enable the browser to parse an XML document, the server must set
14098  * the Content-Type header in the HTTP response to "text/xml".
14099  * @constructor
14100  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14101  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14102  * will be used to make the request.
14103  */
14104 Roo.data.HttpProxy = function(conn){
14105     Roo.data.HttpProxy.superclass.constructor.call(this);
14106     // is conn a conn config or a real conn?
14107     this.conn = conn;
14108     this.useAjax = !conn || !conn.events;
14109   
14110 };
14111
14112 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14113     // thse are take from connection...
14114     
14115     /**
14116      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14117      */
14118     /**
14119      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14120      * extra parameters to each request made by this object. (defaults to undefined)
14121      */
14122     /**
14123      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14124      *  to each request made by this object. (defaults to undefined)
14125      */
14126     /**
14127      * @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)
14128      */
14129     /**
14130      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14131      */
14132      /**
14133      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14134      * @type Boolean
14135      */
14136   
14137
14138     /**
14139      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14140      * @type Boolean
14141      */
14142     /**
14143      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14144      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14145      * a finer-grained basis than the DataProxy events.
14146      */
14147     getConnection : function(){
14148         return this.useAjax ? Roo.Ajax : this.conn;
14149     },
14150
14151     /**
14152      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14153      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14154      * process that block using the passed callback.
14155      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14156      * for the request to the remote server.
14157      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14158      * object into a block of Roo.data.Records.
14159      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14160      * The function must be passed <ul>
14161      * <li>The Record block object</li>
14162      * <li>The "arg" argument from the load function</li>
14163      * <li>A boolean success indicator</li>
14164      * </ul>
14165      * @param {Object} scope The scope in which to call the callback
14166      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14167      */
14168     load : function(params, reader, callback, scope, arg){
14169         if(this.fireEvent("beforeload", this, params) !== false){
14170             var  o = {
14171                 params : params || {},
14172                 request: {
14173                     callback : callback,
14174                     scope : scope,
14175                     arg : arg
14176                 },
14177                 reader: reader,
14178                 callback : this.loadResponse,
14179                 scope: this
14180             };
14181             if(this.useAjax){
14182                 Roo.applyIf(o, this.conn);
14183                 if(this.activeRequest){
14184                     Roo.Ajax.abort(this.activeRequest);
14185                 }
14186                 this.activeRequest = Roo.Ajax.request(o);
14187             }else{
14188                 this.conn.request(o);
14189             }
14190         }else{
14191             callback.call(scope||this, null, arg, false);
14192         }
14193     },
14194
14195     // private
14196     loadResponse : function(o, success, response){
14197         delete this.activeRequest;
14198         if(!success){
14199             this.fireEvent("loadexception", this, o, response);
14200             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14201             return;
14202         }
14203         var result;
14204         try {
14205             result = o.reader.read(response);
14206         }catch(e){
14207             this.fireEvent("loadexception", this, o, response, e);
14208             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14209             return;
14210         }
14211         
14212         this.fireEvent("load", this, o, o.request.arg);
14213         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14214     },
14215
14216     // private
14217     update : function(dataSet){
14218
14219     },
14220
14221     // private
14222     updateResponse : function(dataSet){
14223
14224     }
14225 });/*
14226  * Based on:
14227  * Ext JS Library 1.1.1
14228  * Copyright(c) 2006-2007, Ext JS, LLC.
14229  *
14230  * Originally Released Under LGPL - original licence link has changed is not relivant.
14231  *
14232  * Fork - LGPL
14233  * <script type="text/javascript">
14234  */
14235
14236 /**
14237  * @class Roo.data.ScriptTagProxy
14238  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14239  * other than the originating domain of the running page.<br><br>
14240  * <p>
14241  * <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
14242  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14243  * <p>
14244  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14245  * source code that is used as the source inside a &lt;script> tag.<br><br>
14246  * <p>
14247  * In order for the browser to process the returned data, the server must wrap the data object
14248  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14249  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14250  * depending on whether the callback name was passed:
14251  * <p>
14252  * <pre><code>
14253 boolean scriptTag = false;
14254 String cb = request.getParameter("callback");
14255 if (cb != null) {
14256     scriptTag = true;
14257     response.setContentType("text/javascript");
14258 } else {
14259     response.setContentType("application/x-json");
14260 }
14261 Writer out = response.getWriter();
14262 if (scriptTag) {
14263     out.write(cb + "(");
14264 }
14265 out.print(dataBlock.toJsonString());
14266 if (scriptTag) {
14267     out.write(");");
14268 }
14269 </pre></code>
14270  *
14271  * @constructor
14272  * @param {Object} config A configuration object.
14273  */
14274 Roo.data.ScriptTagProxy = function(config){
14275     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14276     Roo.apply(this, config);
14277     this.head = document.getElementsByTagName("head")[0];
14278 };
14279
14280 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14281
14282 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14283     /**
14284      * @cfg {String} url The URL from which to request the data object.
14285      */
14286     /**
14287      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14288      */
14289     timeout : 30000,
14290     /**
14291      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14292      * the server the name of the callback function set up by the load call to process the returned data object.
14293      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14294      * javascript output which calls this named function passing the data object as its only parameter.
14295      */
14296     callbackParam : "callback",
14297     /**
14298      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14299      * name to the request.
14300      */
14301     nocache : true,
14302
14303     /**
14304      * Load data from the configured URL, read the data object into
14305      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14306      * process that block using the passed callback.
14307      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14308      * for the request to the remote server.
14309      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14310      * object into a block of Roo.data.Records.
14311      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14312      * The function must be passed <ul>
14313      * <li>The Record block object</li>
14314      * <li>The "arg" argument from the load function</li>
14315      * <li>A boolean success indicator</li>
14316      * </ul>
14317      * @param {Object} scope The scope in which to call the callback
14318      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14319      */
14320     load : function(params, reader, callback, scope, arg){
14321         if(this.fireEvent("beforeload", this, params) !== false){
14322
14323             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14324
14325             var url = this.url;
14326             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14327             if(this.nocache){
14328                 url += "&_dc=" + (new Date().getTime());
14329             }
14330             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14331             var trans = {
14332                 id : transId,
14333                 cb : "stcCallback"+transId,
14334                 scriptId : "stcScript"+transId,
14335                 params : params,
14336                 arg : arg,
14337                 url : url,
14338                 callback : callback,
14339                 scope : scope,
14340                 reader : reader
14341             };
14342             var conn = this;
14343
14344             window[trans.cb] = function(o){
14345                 conn.handleResponse(o, trans);
14346             };
14347
14348             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14349
14350             if(this.autoAbort !== false){
14351                 this.abort();
14352             }
14353
14354             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14355
14356             var script = document.createElement("script");
14357             script.setAttribute("src", url);
14358             script.setAttribute("type", "text/javascript");
14359             script.setAttribute("id", trans.scriptId);
14360             this.head.appendChild(script);
14361
14362             this.trans = trans;
14363         }else{
14364             callback.call(scope||this, null, arg, false);
14365         }
14366     },
14367
14368     // private
14369     isLoading : function(){
14370         return this.trans ? true : false;
14371     },
14372
14373     /**
14374      * Abort the current server request.
14375      */
14376     abort : function(){
14377         if(this.isLoading()){
14378             this.destroyTrans(this.trans);
14379         }
14380     },
14381
14382     // private
14383     destroyTrans : function(trans, isLoaded){
14384         this.head.removeChild(document.getElementById(trans.scriptId));
14385         clearTimeout(trans.timeoutId);
14386         if(isLoaded){
14387             window[trans.cb] = undefined;
14388             try{
14389                 delete window[trans.cb];
14390             }catch(e){}
14391         }else{
14392             // if hasn't been loaded, wait for load to remove it to prevent script error
14393             window[trans.cb] = function(){
14394                 window[trans.cb] = undefined;
14395                 try{
14396                     delete window[trans.cb];
14397                 }catch(e){}
14398             };
14399         }
14400     },
14401
14402     // private
14403     handleResponse : function(o, trans){
14404         this.trans = false;
14405         this.destroyTrans(trans, true);
14406         var result;
14407         try {
14408             result = trans.reader.readRecords(o);
14409         }catch(e){
14410             this.fireEvent("loadexception", this, o, trans.arg, e);
14411             trans.callback.call(trans.scope||window, null, trans.arg, false);
14412             return;
14413         }
14414         this.fireEvent("load", this, o, trans.arg);
14415         trans.callback.call(trans.scope||window, result, trans.arg, true);
14416     },
14417
14418     // private
14419     handleFailure : function(trans){
14420         this.trans = false;
14421         this.destroyTrans(trans, false);
14422         this.fireEvent("loadexception", this, null, trans.arg);
14423         trans.callback.call(trans.scope||window, null, trans.arg, false);
14424     }
14425 });/*
14426  * Based on:
14427  * Ext JS Library 1.1.1
14428  * Copyright(c) 2006-2007, Ext JS, LLC.
14429  *
14430  * Originally Released Under LGPL - original licence link has changed is not relivant.
14431  *
14432  * Fork - LGPL
14433  * <script type="text/javascript">
14434  */
14435
14436 /**
14437  * @class Roo.data.JsonReader
14438  * @extends Roo.data.DataReader
14439  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14440  * based on mappings in a provided Roo.data.Record constructor.
14441  * 
14442  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14443  * in the reply previously. 
14444  * 
14445  * <p>
14446  * Example code:
14447  * <pre><code>
14448 var RecordDef = Roo.data.Record.create([
14449     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14450     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14451 ]);
14452 var myReader = new Roo.data.JsonReader({
14453     totalProperty: "results",    // The property which contains the total dataset size (optional)
14454     root: "rows",                // The property which contains an Array of row objects
14455     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14456 }, RecordDef);
14457 </code></pre>
14458  * <p>
14459  * This would consume a JSON file like this:
14460  * <pre><code>
14461 { 'results': 2, 'rows': [
14462     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14463     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14464 }
14465 </code></pre>
14466  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14467  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14468  * paged from the remote server.
14469  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14470  * @cfg {String} root name of the property which contains the Array of row objects.
14471  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14472  * @cfg {Array} fields Array of field definition objects
14473  * @constructor
14474  * Create a new JsonReader
14475  * @param {Object} meta Metadata configuration options
14476  * @param {Object} recordType Either an Array of field definition objects,
14477  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14478  */
14479 Roo.data.JsonReader = function(meta, recordType){
14480     
14481     meta = meta || {};
14482     // set some defaults:
14483     Roo.applyIf(meta, {
14484         totalProperty: 'total',
14485         successProperty : 'success',
14486         root : 'data',
14487         id : 'id'
14488     });
14489     
14490     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14491 };
14492 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14493     
14494     readerType : 'Json',
14495     
14496     /**
14497      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14498      * Used by Store query builder to append _requestMeta to params.
14499      * 
14500      */
14501     metaFromRemote : false,
14502     /**
14503      * This method is only used by a DataProxy which has retrieved data from a remote server.
14504      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14505      * @return {Object} data A data block which is used by an Roo.data.Store object as
14506      * a cache of Roo.data.Records.
14507      */
14508     read : function(response){
14509         var json = response.responseText;
14510        
14511         var o = /* eval:var:o */ eval("("+json+")");
14512         if(!o) {
14513             throw {message: "JsonReader.read: Json object not found"};
14514         }
14515         
14516         if(o.metaData){
14517             
14518             delete this.ef;
14519             this.metaFromRemote = true;
14520             this.meta = o.metaData;
14521             this.recordType = Roo.data.Record.create(o.metaData.fields);
14522             this.onMetaChange(this.meta, this.recordType, o);
14523         }
14524         return this.readRecords(o);
14525     },
14526
14527     // private function a store will implement
14528     onMetaChange : function(meta, recordType, o){
14529
14530     },
14531
14532     /**
14533          * @ignore
14534          */
14535     simpleAccess: function(obj, subsc) {
14536         return obj[subsc];
14537     },
14538
14539         /**
14540          * @ignore
14541          */
14542     getJsonAccessor: function(){
14543         var re = /[\[\.]/;
14544         return function(expr) {
14545             try {
14546                 return(re.test(expr))
14547                     ? new Function("obj", "return obj." + expr)
14548                     : function(obj){
14549                         return obj[expr];
14550                     };
14551             } catch(e){}
14552             return Roo.emptyFn;
14553         };
14554     }(),
14555
14556     /**
14557      * Create a data block containing Roo.data.Records from an XML document.
14558      * @param {Object} o An object which contains an Array of row objects in the property specified
14559      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14560      * which contains the total size of the dataset.
14561      * @return {Object} data A data block which is used by an Roo.data.Store object as
14562      * a cache of Roo.data.Records.
14563      */
14564     readRecords : function(o){
14565         /**
14566          * After any data loads, the raw JSON data is available for further custom processing.
14567          * @type Object
14568          */
14569         this.o = o;
14570         var s = this.meta, Record = this.recordType,
14571             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14572
14573 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14574         if (!this.ef) {
14575             if(s.totalProperty) {
14576                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14577                 }
14578                 if(s.successProperty) {
14579                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14580                 }
14581                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14582                 if (s.id) {
14583                         var g = this.getJsonAccessor(s.id);
14584                         this.getId = function(rec) {
14585                                 var r = g(rec);  
14586                                 return (r === undefined || r === "") ? null : r;
14587                         };
14588                 } else {
14589                         this.getId = function(){return null;};
14590                 }
14591             this.ef = [];
14592             for(var jj = 0; jj < fl; jj++){
14593                 f = fi[jj];
14594                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14595                 this.ef[jj] = this.getJsonAccessor(map);
14596             }
14597         }
14598
14599         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14600         if(s.totalProperty){
14601             var vt = parseInt(this.getTotal(o), 10);
14602             if(!isNaN(vt)){
14603                 totalRecords = vt;
14604             }
14605         }
14606         if(s.successProperty){
14607             var vs = this.getSuccess(o);
14608             if(vs === false || vs === 'false'){
14609                 success = false;
14610             }
14611         }
14612         var records = [];
14613         for(var i = 0; i < c; i++){
14614                 var n = root[i];
14615             var values = {};
14616             var id = this.getId(n);
14617             for(var j = 0; j < fl; j++){
14618                 f = fi[j];
14619             var v = this.ef[j](n);
14620             if (!f.convert) {
14621                 Roo.log('missing convert for ' + f.name);
14622                 Roo.log(f);
14623                 continue;
14624             }
14625             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14626             }
14627             var record = new Record(values, id);
14628             record.json = n;
14629             records[i] = record;
14630         }
14631         return {
14632             raw : o,
14633             success : success,
14634             records : records,
14635             totalRecords : totalRecords
14636         };
14637     },
14638     // used when loading children.. @see loadDataFromChildren
14639     toLoadData: function(rec)
14640     {
14641         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14642         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14643         return { data : data, total : data.length };
14644         
14645     }
14646 });/*
14647  * Based on:
14648  * Ext JS Library 1.1.1
14649  * Copyright(c) 2006-2007, Ext JS, LLC.
14650  *
14651  * Originally Released Under LGPL - original licence link has changed is not relivant.
14652  *
14653  * Fork - LGPL
14654  * <script type="text/javascript">
14655  */
14656
14657 /**
14658  * @class Roo.data.ArrayReader
14659  * @extends Roo.data.DataReader
14660  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14661  * Each element of that Array represents a row of data fields. The
14662  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14663  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14664  * <p>
14665  * Example code:.
14666  * <pre><code>
14667 var RecordDef = Roo.data.Record.create([
14668     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14669     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14670 ]);
14671 var myReader = new Roo.data.ArrayReader({
14672     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14673 }, RecordDef);
14674 </code></pre>
14675  * <p>
14676  * This would consume an Array like this:
14677  * <pre><code>
14678 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14679   </code></pre>
14680  
14681  * @constructor
14682  * Create a new JsonReader
14683  * @param {Object} meta Metadata configuration options.
14684  * @param {Object|Array} recordType Either an Array of field definition objects
14685  * 
14686  * @cfg {Array} fields Array of field definition objects
14687  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14688  * as specified to {@link Roo.data.Record#create},
14689  * or an {@link Roo.data.Record} object
14690  *
14691  * 
14692  * created using {@link Roo.data.Record#create}.
14693  */
14694 Roo.data.ArrayReader = function(meta, recordType)
14695 {    
14696     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14697 };
14698
14699 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14700     
14701       /**
14702      * Create a data block containing Roo.data.Records from an XML document.
14703      * @param {Object} o An Array of row objects which represents the dataset.
14704      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14705      * a cache of Roo.data.Records.
14706      */
14707     readRecords : function(o)
14708     {
14709         var sid = this.meta ? this.meta.id : null;
14710         var recordType = this.recordType, fields = recordType.prototype.fields;
14711         var records = [];
14712         var root = o;
14713         for(var i = 0; i < root.length; i++){
14714                 var n = root[i];
14715             var values = {};
14716             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14717             for(var j = 0, jlen = fields.length; j < jlen; j++){
14718                 var f = fields.items[j];
14719                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14720                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14721                 v = f.convert(v);
14722                 values[f.name] = v;
14723             }
14724             var record = new recordType(values, id);
14725             record.json = n;
14726             records[records.length] = record;
14727         }
14728         return {
14729             records : records,
14730             totalRecords : records.length
14731         };
14732     },
14733     // used when loading children.. @see loadDataFromChildren
14734     toLoadData: function(rec)
14735     {
14736         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14737         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14738         
14739     }
14740     
14741     
14742 });/*
14743  * - LGPL
14744  * * 
14745  */
14746
14747 /**
14748  * @class Roo.bootstrap.ComboBox
14749  * @extends Roo.bootstrap.TriggerField
14750  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14751  * @cfg {Boolean} append (true|false) default false
14752  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14753  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14754  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14755  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14756  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14757  * @cfg {Boolean} animate default true
14758  * @cfg {Boolean} emptyResultText only for touch device
14759  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14760  * @cfg {String} emptyTitle default ''
14761  * @cfg {Number} width fixed with? experimental
14762  * @constructor
14763  * Create a new ComboBox.
14764  * @param {Object} config Configuration options
14765  */
14766 Roo.bootstrap.ComboBox = function(config){
14767     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14768     this.addEvents({
14769         /**
14770          * @event expand
14771          * Fires when the dropdown list is expanded
14772         * @param {Roo.bootstrap.ComboBox} combo This combo box
14773         */
14774         'expand' : true,
14775         /**
14776          * @event collapse
14777          * Fires when the dropdown list is collapsed
14778         * @param {Roo.bootstrap.ComboBox} combo This combo box
14779         */
14780         'collapse' : true,
14781         /**
14782          * @event beforeselect
14783          * Fires before a list item is selected. Return false to cancel the selection.
14784         * @param {Roo.bootstrap.ComboBox} combo This combo box
14785         * @param {Roo.data.Record} record The data record returned from the underlying store
14786         * @param {Number} index The index of the selected item in the dropdown list
14787         */
14788         'beforeselect' : true,
14789         /**
14790          * @event select
14791          * Fires when a list item is selected
14792         * @param {Roo.bootstrap.ComboBox} combo This combo box
14793         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14794         * @param {Number} index The index of the selected item in the dropdown list
14795         */
14796         'select' : true,
14797         /**
14798          * @event beforequery
14799          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14800          * The event object passed has these properties:
14801         * @param {Roo.bootstrap.ComboBox} combo This combo box
14802         * @param {String} query The query
14803         * @param {Boolean} forceAll true to force "all" query
14804         * @param {Boolean} cancel true to cancel the query
14805         * @param {Object} e The query event object
14806         */
14807         'beforequery': true,
14808          /**
14809          * @event add
14810          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14811         * @param {Roo.bootstrap.ComboBox} combo This combo box
14812         */
14813         'add' : true,
14814         /**
14815          * @event edit
14816          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14817         * @param {Roo.bootstrap.ComboBox} combo This combo box
14818         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14819         */
14820         'edit' : true,
14821         /**
14822          * @event remove
14823          * Fires when the remove value from the combobox array
14824         * @param {Roo.bootstrap.ComboBox} combo This combo box
14825         */
14826         'remove' : true,
14827         /**
14828          * @event afterremove
14829          * Fires when the remove value from the combobox array
14830         * @param {Roo.bootstrap.ComboBox} combo This combo box
14831         */
14832         'afterremove' : true,
14833         /**
14834          * @event specialfilter
14835          * Fires when specialfilter
14836             * @param {Roo.bootstrap.ComboBox} combo This combo box
14837             */
14838         'specialfilter' : true,
14839         /**
14840          * @event tick
14841          * Fires when tick the element
14842             * @param {Roo.bootstrap.ComboBox} combo This combo box
14843             */
14844         'tick' : true,
14845         /**
14846          * @event touchviewdisplay
14847          * Fires when touch view require special display (default is using displayField)
14848             * @param {Roo.bootstrap.ComboBox} combo This combo box
14849             * @param {Object} cfg set html .
14850             */
14851         'touchviewdisplay' : true
14852         
14853     });
14854     
14855     this.item = [];
14856     this.tickItems = [];
14857     
14858     this.selectedIndex = -1;
14859     if(this.mode == 'local'){
14860         if(config.queryDelay === undefined){
14861             this.queryDelay = 10;
14862         }
14863         if(config.minChars === undefined){
14864             this.minChars = 0;
14865         }
14866     }
14867 };
14868
14869 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14870      
14871     /**
14872      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14873      * rendering into an Roo.Editor, defaults to false)
14874      */
14875     /**
14876      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14877      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14878      */
14879     /**
14880      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14881      */
14882     /**
14883      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14884      * the dropdown list (defaults to undefined, with no header element)
14885      */
14886
14887      /**
14888      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
14889      */
14890      
14891      /**
14892      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14893      */
14894     listWidth: undefined,
14895     /**
14896      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14897      * mode = 'remote' or 'text' if mode = 'local')
14898      */
14899     displayField: undefined,
14900     
14901     /**
14902      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14903      * mode = 'remote' or 'value' if mode = 'local'). 
14904      * Note: use of a valueField requires the user make a selection
14905      * in order for a value to be mapped.
14906      */
14907     valueField: undefined,
14908     /**
14909      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14910      */
14911     modalTitle : '',
14912     
14913     /**
14914      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14915      * field's data value (defaults to the underlying DOM element's name)
14916      */
14917     hiddenName: undefined,
14918     /**
14919      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14920      */
14921     listClass: '',
14922     /**
14923      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14924      */
14925     selectedClass: 'active',
14926     
14927     /**
14928      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14929      */
14930     shadow:'sides',
14931     /**
14932      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14933      * anchor positions (defaults to 'tl-bl')
14934      */
14935     listAlign: 'tl-bl?',
14936     /**
14937      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14938      */
14939     maxHeight: 300,
14940     /**
14941      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
14942      * query specified by the allQuery config option (defaults to 'query')
14943      */
14944     triggerAction: 'query',
14945     /**
14946      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14947      * (defaults to 4, does not apply if editable = false)
14948      */
14949     minChars : 4,
14950     /**
14951      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14952      * delay (typeAheadDelay) if it matches a known value (defaults to false)
14953      */
14954     typeAhead: false,
14955     /**
14956      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14957      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14958      */
14959     queryDelay: 500,
14960     /**
14961      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14962      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
14963      */
14964     pageSize: 0,
14965     /**
14966      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
14967      * when editable = true (defaults to false)
14968      */
14969     selectOnFocus:false,
14970     /**
14971      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14972      */
14973     queryParam: 'query',
14974     /**
14975      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
14976      * when mode = 'remote' (defaults to 'Loading...')
14977      */
14978     loadingText: 'Loading...',
14979     /**
14980      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14981      */
14982     resizable: false,
14983     /**
14984      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14985      */
14986     handleHeight : 8,
14987     /**
14988      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14989      * traditional select (defaults to true)
14990      */
14991     editable: true,
14992     /**
14993      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14994      */
14995     allQuery: '',
14996     /**
14997      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14998      */
14999     mode: 'remote',
15000     /**
15001      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15002      * listWidth has a higher value)
15003      */
15004     minListWidth : 70,
15005     /**
15006      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15007      * allow the user to set arbitrary text into the field (defaults to false)
15008      */
15009     forceSelection:false,
15010     /**
15011      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15012      * if typeAhead = true (defaults to 250)
15013      */
15014     typeAheadDelay : 250,
15015     /**
15016      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15017      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15018      */
15019     valueNotFoundText : undefined,
15020     /**
15021      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15022      */
15023     blockFocus : false,
15024     
15025     /**
15026      * @cfg {Boolean} disableClear Disable showing of clear button.
15027      */
15028     disableClear : false,
15029     /**
15030      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15031      */
15032     alwaysQuery : false,
15033     
15034     /**
15035      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15036      */
15037     multiple : false,
15038     
15039     /**
15040      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15041      */
15042     invalidClass : "has-warning",
15043     
15044     /**
15045      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15046      */
15047     validClass : "has-success",
15048     
15049     /**
15050      * @cfg {Boolean} specialFilter (true|false) special filter default false
15051      */
15052     specialFilter : false,
15053     
15054     /**
15055      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15056      */
15057     mobileTouchView : true,
15058     
15059     /**
15060      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15061      */
15062     useNativeIOS : false,
15063     
15064     /**
15065      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15066      */
15067     mobile_restrict_height : false,
15068     
15069     ios_options : false,
15070     
15071     //private
15072     addicon : false,
15073     editicon: false,
15074     
15075     page: 0,
15076     hasQuery: false,
15077     append: false,
15078     loadNext: false,
15079     autoFocus : true,
15080     tickable : false,
15081     btnPosition : 'right',
15082     triggerList : true,
15083     showToggleBtn : true,
15084     animate : true,
15085     emptyResultText: 'Empty',
15086     triggerText : 'Select',
15087     emptyTitle : '',
15088     width : false,
15089     
15090     // element that contains real text value.. (when hidden is used..)
15091     
15092     getAutoCreate : function()
15093     {   
15094         var cfg = false;
15095         //render
15096         /*
15097          * Render classic select for iso
15098          */
15099         
15100         if(Roo.isIOS && this.useNativeIOS){
15101             cfg = this.getAutoCreateNativeIOS();
15102             return cfg;
15103         }
15104         
15105         /*
15106          * Touch Devices
15107          */
15108         
15109         if(Roo.isTouch && this.mobileTouchView){
15110             cfg = this.getAutoCreateTouchView();
15111             return cfg;;
15112         }
15113         
15114         /*
15115          *  Normal ComboBox
15116          */
15117         if(!this.tickable){
15118             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15119             return cfg;
15120         }
15121         
15122         /*
15123          *  ComboBox with tickable selections
15124          */
15125              
15126         var align = this.labelAlign || this.parentLabelAlign();
15127         
15128         cfg = {
15129             cls : 'form-group roo-combobox-tickable' //input-group
15130         };
15131         
15132         var btn_text_select = '';
15133         var btn_text_done = '';
15134         var btn_text_cancel = '';
15135         
15136         if (this.btn_text_show) {
15137             btn_text_select = 'Select';
15138             btn_text_done = 'Done';
15139             btn_text_cancel = 'Cancel'; 
15140         }
15141         
15142         var buttons = {
15143             tag : 'div',
15144             cls : 'tickable-buttons',
15145             cn : [
15146                 {
15147                     tag : 'button',
15148                     type : 'button',
15149                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15150                     //html : this.triggerText
15151                     html: btn_text_select
15152                 },
15153                 {
15154                     tag : 'button',
15155                     type : 'button',
15156                     name : 'ok',
15157                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15158                     //html : 'Done'
15159                     html: btn_text_done
15160                 },
15161                 {
15162                     tag : 'button',
15163                     type : 'button',
15164                     name : 'cancel',
15165                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15166                     //html : 'Cancel'
15167                     html: btn_text_cancel
15168                 }
15169             ]
15170         };
15171         
15172         if(this.editable){
15173             buttons.cn.unshift({
15174                 tag: 'input',
15175                 cls: 'roo-select2-search-field-input'
15176             });
15177         }
15178         
15179         var _this = this;
15180         
15181         Roo.each(buttons.cn, function(c){
15182             if (_this.size) {
15183                 c.cls += ' btn-' + _this.size;
15184             }
15185
15186             if (_this.disabled) {
15187                 c.disabled = true;
15188             }
15189         });
15190         
15191         var box = {
15192             tag: 'div',
15193             style : 'display: contents',
15194             cn: [
15195                 {
15196                     tag: 'input',
15197                     type : 'hidden',
15198                     cls: 'form-hidden-field'
15199                 },
15200                 {
15201                     tag: 'ul',
15202                     cls: 'roo-select2-choices',
15203                     cn:[
15204                         {
15205                             tag: 'li',
15206                             cls: 'roo-select2-search-field',
15207                             cn: [
15208                                 buttons
15209                             ]
15210                         }
15211                     ]
15212                 }
15213             ]
15214         };
15215         
15216         var combobox = {
15217             cls: 'roo-select2-container input-group roo-select2-container-multi',
15218             cn: [
15219                 
15220                 box
15221 //                {
15222 //                    tag: 'ul',
15223 //                    cls: 'typeahead typeahead-long dropdown-menu',
15224 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15225 //                }
15226             ]
15227         };
15228         
15229         if(this.hasFeedback && !this.allowBlank){
15230             
15231             var feedback = {
15232                 tag: 'span',
15233                 cls: 'glyphicon form-control-feedback'
15234             };
15235
15236             combobox.cn.push(feedback);
15237         }
15238         
15239         
15240         
15241         var indicator = {
15242             tag : 'i',
15243             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15244             tooltip : 'This field is required'
15245         };
15246         if (Roo.bootstrap.version == 4) {
15247             indicator = {
15248                 tag : 'i',
15249                 style : 'display:none'
15250             };
15251         }
15252         if (align ==='left' && this.fieldLabel.length) {
15253             
15254             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15255             
15256             cfg.cn = [
15257                 indicator,
15258                 {
15259                     tag: 'label',
15260                     'for' :  id,
15261                     cls : 'control-label col-form-label',
15262                     html : this.fieldLabel
15263
15264                 },
15265                 {
15266                     cls : "", 
15267                     cn: [
15268                         combobox
15269                     ]
15270                 }
15271
15272             ];
15273             
15274             var labelCfg = cfg.cn[1];
15275             var contentCfg = cfg.cn[2];
15276             
15277
15278             if(this.indicatorpos == 'right'){
15279                 
15280                 cfg.cn = [
15281                     {
15282                         tag: 'label',
15283                         'for' :  id,
15284                         cls : 'control-label col-form-label',
15285                         cn : [
15286                             {
15287                                 tag : 'span',
15288                                 html : this.fieldLabel
15289                             },
15290                             indicator
15291                         ]
15292                     },
15293                     {
15294                         cls : "",
15295                         cn: [
15296                             combobox
15297                         ]
15298                     }
15299
15300                 ];
15301                 
15302                 
15303                 
15304                 labelCfg = cfg.cn[0];
15305                 contentCfg = cfg.cn[1];
15306             
15307             }
15308             
15309             if(this.labelWidth > 12){
15310                 labelCfg.style = "width: " + this.labelWidth + 'px';
15311             }
15312             if(this.width * 1 > 0){
15313                 contentCfg.style = "width: " + this.width + 'px';
15314             }
15315             if(this.labelWidth < 13 && this.labelmd == 0){
15316                 this.labelmd = this.labelWidth;
15317             }
15318             
15319             if(this.labellg > 0){
15320                 labelCfg.cls += ' col-lg-' + this.labellg;
15321                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15322             }
15323             
15324             if(this.labelmd > 0){
15325                 labelCfg.cls += ' col-md-' + this.labelmd;
15326                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15327             }
15328             
15329             if(this.labelsm > 0){
15330                 labelCfg.cls += ' col-sm-' + this.labelsm;
15331                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15332             }
15333             
15334             if(this.labelxs > 0){
15335                 labelCfg.cls += ' col-xs-' + this.labelxs;
15336                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15337             }
15338                 
15339                 
15340         } else if ( this.fieldLabel.length) {
15341 //                Roo.log(" label");
15342                  cfg.cn = [
15343                    indicator,
15344                     {
15345                         tag: 'label',
15346                         //cls : 'input-group-addon',
15347                         html : this.fieldLabel
15348                     },
15349                     combobox
15350                 ];
15351                 
15352                 if(this.indicatorpos == 'right'){
15353                     cfg.cn = [
15354                         {
15355                             tag: 'label',
15356                             //cls : 'input-group-addon',
15357                             html : this.fieldLabel
15358                         },
15359                         indicator,
15360                         combobox
15361                     ];
15362                     
15363                 }
15364
15365         } else {
15366             
15367 //                Roo.log(" no label && no align");
15368                 cfg = combobox
15369                      
15370                 
15371         }
15372          
15373         var settings=this;
15374         ['xs','sm','md','lg'].map(function(size){
15375             if (settings[size]) {
15376                 cfg.cls += ' col-' + size + '-' + settings[size];
15377             }
15378         });
15379         
15380         return cfg;
15381         
15382     },
15383     
15384     _initEventsCalled : false,
15385     
15386     // private
15387     initEvents: function()
15388     {   
15389         if (this._initEventsCalled) { // as we call render... prevent looping...
15390             return;
15391         }
15392         this._initEventsCalled = true;
15393         
15394         if (!this.store) {
15395             throw "can not find store for combo";
15396         }
15397         
15398         this.indicator = this.indicatorEl();
15399         
15400         this.store = Roo.factory(this.store, Roo.data);
15401         this.store.parent = this;
15402         
15403         // if we are building from html. then this element is so complex, that we can not really
15404         // use the rendered HTML.
15405         // so we have to trash and replace the previous code.
15406         if (Roo.XComponent.build_from_html) {
15407             // remove this element....
15408             var e = this.el.dom, k=0;
15409             while (e ) { e = e.previousSibling;  ++k;}
15410
15411             this.el.remove();
15412             
15413             this.el=false;
15414             this.rendered = false;
15415             
15416             this.render(this.parent().getChildContainer(true), k);
15417         }
15418         
15419         if(Roo.isIOS && this.useNativeIOS){
15420             this.initIOSView();
15421             return;
15422         }
15423         
15424         /*
15425          * Touch Devices
15426          */
15427         
15428         if(Roo.isTouch && this.mobileTouchView){
15429             this.initTouchView();
15430             return;
15431         }
15432         
15433         if(this.tickable){
15434             this.initTickableEvents();
15435             return;
15436         }
15437         
15438         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15439         
15440         if(this.hiddenName){
15441             
15442             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15443             
15444             this.hiddenField.dom.value =
15445                 this.hiddenValue !== undefined ? this.hiddenValue :
15446                 this.value !== undefined ? this.value : '';
15447
15448             // prevent input submission
15449             this.el.dom.removeAttribute('name');
15450             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15451              
15452              
15453         }
15454         //if(Roo.isGecko){
15455         //    this.el.dom.setAttribute('autocomplete', 'off');
15456         //}
15457         
15458         var cls = 'x-combo-list';
15459         
15460         //this.list = new Roo.Layer({
15461         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15462         //});
15463         
15464         var _this = this;
15465         
15466         (function(){
15467             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15468             _this.list.setWidth(lw);
15469         }).defer(100);
15470         
15471         this.list.on('mouseover', this.onViewOver, this);
15472         this.list.on('mousemove', this.onViewMove, this);
15473         this.list.on('scroll', this.onViewScroll, this);
15474         
15475         /*
15476         this.list.swallowEvent('mousewheel');
15477         this.assetHeight = 0;
15478
15479         if(this.title){
15480             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15481             this.assetHeight += this.header.getHeight();
15482         }
15483
15484         this.innerList = this.list.createChild({cls:cls+'-inner'});
15485         this.innerList.on('mouseover', this.onViewOver, this);
15486         this.innerList.on('mousemove', this.onViewMove, this);
15487         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15488         
15489         if(this.allowBlank && !this.pageSize && !this.disableClear){
15490             this.footer = this.list.createChild({cls:cls+'-ft'});
15491             this.pageTb = new Roo.Toolbar(this.footer);
15492            
15493         }
15494         if(this.pageSize){
15495             this.footer = this.list.createChild({cls:cls+'-ft'});
15496             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15497                     {pageSize: this.pageSize});
15498             
15499         }
15500         
15501         if (this.pageTb && this.allowBlank && !this.disableClear) {
15502             var _this = this;
15503             this.pageTb.add(new Roo.Toolbar.Fill(), {
15504                 cls: 'x-btn-icon x-btn-clear',
15505                 text: '&#160;',
15506                 handler: function()
15507                 {
15508                     _this.collapse();
15509                     _this.clearValue();
15510                     _this.onSelect(false, -1);
15511                 }
15512             });
15513         }
15514         if (this.footer) {
15515             this.assetHeight += this.footer.getHeight();
15516         }
15517         */
15518             
15519         if(!this.tpl){
15520             this.tpl = Roo.bootstrap.version == 4 ?
15521                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15522                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15523         }
15524
15525         this.view = new Roo.View(this.list, this.tpl, {
15526             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15527         });
15528         //this.view.wrapEl.setDisplayed(false);
15529         this.view.on('click', this.onViewClick, this);
15530         
15531         
15532         this.store.on('beforeload', this.onBeforeLoad, this);
15533         this.store.on('load', this.onLoad, this);
15534         this.store.on('loadexception', this.onLoadException, this);
15535         /*
15536         if(this.resizable){
15537             this.resizer = new Roo.Resizable(this.list,  {
15538                pinned:true, handles:'se'
15539             });
15540             this.resizer.on('resize', function(r, w, h){
15541                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15542                 this.listWidth = w;
15543                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15544                 this.restrictHeight();
15545             }, this);
15546             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15547         }
15548         */
15549         if(!this.editable){
15550             this.editable = true;
15551             this.setEditable(false);
15552         }
15553         
15554         /*
15555         
15556         if (typeof(this.events.add.listeners) != 'undefined') {
15557             
15558             this.addicon = this.wrap.createChild(
15559                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15560        
15561             this.addicon.on('click', function(e) {
15562                 this.fireEvent('add', this);
15563             }, this);
15564         }
15565         if (typeof(this.events.edit.listeners) != 'undefined') {
15566             
15567             this.editicon = this.wrap.createChild(
15568                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15569             if (this.addicon) {
15570                 this.editicon.setStyle('margin-left', '40px');
15571             }
15572             this.editicon.on('click', function(e) {
15573                 
15574                 // we fire even  if inothing is selected..
15575                 this.fireEvent('edit', this, this.lastData );
15576                 
15577             }, this);
15578         }
15579         */
15580         
15581         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15582             "up" : function(e){
15583                 this.inKeyMode = true;
15584                 this.selectPrev();
15585             },
15586
15587             "down" : function(e){
15588                 if(!this.isExpanded()){
15589                     this.onTriggerClick();
15590                 }else{
15591                     this.inKeyMode = true;
15592                     this.selectNext();
15593                 }
15594             },
15595
15596             "enter" : function(e){
15597 //                this.onViewClick();
15598                 //return true;
15599                 this.collapse();
15600                 
15601                 if(this.fireEvent("specialkey", this, e)){
15602                     this.onViewClick(false);
15603                 }
15604                 
15605                 return true;
15606             },
15607
15608             "esc" : function(e){
15609                 this.collapse();
15610             },
15611
15612             "tab" : function(e){
15613                 this.collapse();
15614                 
15615                 if(this.fireEvent("specialkey", this, e)){
15616                     this.onViewClick(false);
15617                 }
15618                 
15619                 return true;
15620             },
15621
15622             scope : this,
15623
15624             doRelay : function(foo, bar, hname){
15625                 if(hname == 'down' || this.scope.isExpanded()){
15626                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15627                 }
15628                 return true;
15629             },
15630
15631             forceKeyDown: true
15632         });
15633         
15634         
15635         this.queryDelay = Math.max(this.queryDelay || 10,
15636                 this.mode == 'local' ? 10 : 250);
15637         
15638         
15639         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15640         
15641         if(this.typeAhead){
15642             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15643         }
15644         if(this.editable !== false){
15645             this.inputEl().on("keyup", this.onKeyUp, this);
15646         }
15647         if(this.forceSelection){
15648             this.inputEl().on('blur', this.doForce, this);
15649         }
15650         
15651         if(this.multiple){
15652             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15653             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15654         }
15655     },
15656     
15657     initTickableEvents: function()
15658     {   
15659         this.createList();
15660         
15661         if(this.hiddenName){
15662             
15663             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15664             
15665             this.hiddenField.dom.value =
15666                 this.hiddenValue !== undefined ? this.hiddenValue :
15667                 this.value !== undefined ? this.value : '';
15668
15669             // prevent input submission
15670             this.el.dom.removeAttribute('name');
15671             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15672              
15673              
15674         }
15675         
15676 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15677         
15678         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15679         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15680         if(this.triggerList){
15681             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15682         }
15683          
15684         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15685         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15686         
15687         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15688         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15689         
15690         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15691         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15692         
15693         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15694         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15695         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15696         
15697         this.okBtn.hide();
15698         this.cancelBtn.hide();
15699         
15700         var _this = this;
15701         
15702         (function(){
15703             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15704             _this.list.setWidth(lw);
15705         }).defer(100);
15706         
15707         this.list.on('mouseover', this.onViewOver, this);
15708         this.list.on('mousemove', this.onViewMove, this);
15709         
15710         this.list.on('scroll', this.onViewScroll, this);
15711         
15712         if(!this.tpl){
15713             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15714                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15715         }
15716
15717         this.view = new Roo.View(this.list, this.tpl, {
15718             singleSelect:true,
15719             tickable:true,
15720             parent:this,
15721             store: this.store,
15722             selectedClass: this.selectedClass
15723         });
15724         
15725         //this.view.wrapEl.setDisplayed(false);
15726         this.view.on('click', this.onViewClick, this);
15727         
15728         
15729         
15730         this.store.on('beforeload', this.onBeforeLoad, this);
15731         this.store.on('load', this.onLoad, this);
15732         this.store.on('loadexception', this.onLoadException, this);
15733         
15734         if(this.editable){
15735             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15736                 "up" : function(e){
15737                     this.inKeyMode = true;
15738                     this.selectPrev();
15739                 },
15740
15741                 "down" : function(e){
15742                     this.inKeyMode = true;
15743                     this.selectNext();
15744                 },
15745
15746                 "enter" : function(e){
15747                     if(this.fireEvent("specialkey", this, e)){
15748                         this.onViewClick(false);
15749                     }
15750                     
15751                     return true;
15752                 },
15753
15754                 "esc" : function(e){
15755                     this.onTickableFooterButtonClick(e, false, false);
15756                 },
15757
15758                 "tab" : function(e){
15759                     this.fireEvent("specialkey", this, e);
15760                     
15761                     this.onTickableFooterButtonClick(e, false, false);
15762                     
15763                     return true;
15764                 },
15765
15766                 scope : this,
15767
15768                 doRelay : function(e, fn, key){
15769                     if(this.scope.isExpanded()){
15770                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15771                     }
15772                     return true;
15773                 },
15774
15775                 forceKeyDown: true
15776             });
15777         }
15778         
15779         this.queryDelay = Math.max(this.queryDelay || 10,
15780                 this.mode == 'local' ? 10 : 250);
15781         
15782         
15783         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15784         
15785         if(this.typeAhead){
15786             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15787         }
15788         
15789         if(this.editable !== false){
15790             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15791         }
15792         
15793         this.indicator = this.indicatorEl();
15794         
15795         if(this.indicator){
15796             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15797             this.indicator.hide();
15798         }
15799         
15800     },
15801
15802     onDestroy : function(){
15803         if(this.view){
15804             this.view.setStore(null);
15805             this.view.el.removeAllListeners();
15806             this.view.el.remove();
15807             this.view.purgeListeners();
15808         }
15809         if(this.list){
15810             this.list.dom.innerHTML  = '';
15811         }
15812         
15813         if(this.store){
15814             this.store.un('beforeload', this.onBeforeLoad, this);
15815             this.store.un('load', this.onLoad, this);
15816             this.store.un('loadexception', this.onLoadException, this);
15817         }
15818         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15819     },
15820
15821     // private
15822     fireKey : function(e){
15823         if(e.isNavKeyPress() && !this.list.isVisible()){
15824             this.fireEvent("specialkey", this, e);
15825         }
15826     },
15827
15828     // private
15829     onResize: function(w, h)
15830     {
15831         
15832         
15833 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15834 //        
15835 //        if(typeof w != 'number'){
15836 //            // we do not handle it!?!?
15837 //            return;
15838 //        }
15839 //        var tw = this.trigger.getWidth();
15840 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15841 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15842 //        var x = w - tw;
15843 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15844 //            
15845 //        //this.trigger.setStyle('left', x+'px');
15846 //        
15847 //        if(this.list && this.listWidth === undefined){
15848 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15849 //            this.list.setWidth(lw);
15850 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15851 //        }
15852         
15853     
15854         
15855     },
15856
15857     /**
15858      * Allow or prevent the user from directly editing the field text.  If false is passed,
15859      * the user will only be able to select from the items defined in the dropdown list.  This method
15860      * is the runtime equivalent of setting the 'editable' config option at config time.
15861      * @param {Boolean} value True to allow the user to directly edit the field text
15862      */
15863     setEditable : function(value){
15864         if(value == this.editable){
15865             return;
15866         }
15867         this.editable = value;
15868         if(!value){
15869             this.inputEl().dom.setAttribute('readOnly', true);
15870             this.inputEl().on('mousedown', this.onTriggerClick,  this);
15871             this.inputEl().addClass('x-combo-noedit');
15872         }else{
15873             this.inputEl().dom.setAttribute('readOnly', false);
15874             this.inputEl().un('mousedown', this.onTriggerClick,  this);
15875             this.inputEl().removeClass('x-combo-noedit');
15876         }
15877     },
15878
15879     // private
15880     
15881     onBeforeLoad : function(combo,opts){
15882         if(!this.hasFocus){
15883             return;
15884         }
15885          if (!opts.add) {
15886             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15887          }
15888         this.restrictHeight();
15889         this.selectedIndex = -1;
15890     },
15891
15892     // private
15893     onLoad : function(){
15894         
15895         this.hasQuery = false;
15896         
15897         if(!this.hasFocus){
15898             return;
15899         }
15900         
15901         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15902             this.loading.hide();
15903         }
15904         
15905         if(this.store.getCount() > 0){
15906             
15907             this.expand();
15908             this.restrictHeight();
15909             if(this.lastQuery == this.allQuery){
15910                 if(this.editable && !this.tickable){
15911                     this.inputEl().dom.select();
15912                 }
15913                 
15914                 if(
15915                     !this.selectByValue(this.value, true) &&
15916                     this.autoFocus && 
15917                     (
15918                         !this.store.lastOptions ||
15919                         typeof(this.store.lastOptions.add) == 'undefined' || 
15920                         this.store.lastOptions.add != true
15921                     )
15922                 ){
15923                     this.select(0, true);
15924                 }
15925             }else{
15926                 if(this.autoFocus){
15927                     this.selectNext();
15928                 }
15929                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15930                     this.taTask.delay(this.typeAheadDelay);
15931                 }
15932             }
15933         }else{
15934             this.onEmptyResults();
15935         }
15936         
15937         //this.el.focus();
15938     },
15939     // private
15940     onLoadException : function()
15941     {
15942         this.hasQuery = false;
15943         
15944         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15945             this.loading.hide();
15946         }
15947         
15948         if(this.tickable && this.editable){
15949             return;
15950         }
15951         
15952         this.collapse();
15953         // only causes errors at present
15954         //Roo.log(this.store.reader.jsonData);
15955         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15956             // fixme
15957             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15958         //}
15959         
15960         
15961     },
15962     // private
15963     onTypeAhead : function(){
15964         if(this.store.getCount() > 0){
15965             var r = this.store.getAt(0);
15966             var newValue = r.data[this.displayField];
15967             var len = newValue.length;
15968             var selStart = this.getRawValue().length;
15969             
15970             if(selStart != len){
15971                 this.setRawValue(newValue);
15972                 this.selectText(selStart, newValue.length);
15973             }
15974         }
15975     },
15976
15977     // private
15978     onSelect : function(record, index){
15979         
15980         if(this.fireEvent('beforeselect', this, record, index) !== false){
15981         
15982             this.setFromData(index > -1 ? record.data : false);
15983             
15984             this.collapse();
15985             this.fireEvent('select', this, record, index);
15986         }
15987     },
15988
15989     /**
15990      * Returns the currently selected field value or empty string if no value is set.
15991      * @return {String} value The selected value
15992      */
15993     getValue : function()
15994     {
15995         if(Roo.isIOS && this.useNativeIOS){
15996             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15997         }
15998         
15999         if(this.multiple){
16000             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16001         }
16002         
16003         if(this.valueField){
16004             return typeof this.value != 'undefined' ? this.value : '';
16005         }else{
16006             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16007         }
16008     },
16009     
16010     getRawValue : function()
16011     {
16012         if(Roo.isIOS && this.useNativeIOS){
16013             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16014         }
16015         
16016         var v = this.inputEl().getValue();
16017         
16018         return v;
16019     },
16020
16021     /**
16022      * Clears any text/value currently set in the field
16023      */
16024     clearValue : function(){
16025         
16026         if(this.hiddenField){
16027             this.hiddenField.dom.value = '';
16028         }
16029         this.value = '';
16030         this.setRawValue('');
16031         this.lastSelectionText = '';
16032         this.lastData = false;
16033         
16034         var close = this.closeTriggerEl();
16035         
16036         if(close){
16037             close.hide();
16038         }
16039         
16040         this.validate();
16041         
16042     },
16043
16044     /**
16045      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16046      * will be displayed in the field.  If the value does not match the data value of an existing item,
16047      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16048      * Otherwise the field will be blank (although the value will still be set).
16049      * @param {String} value The value to match
16050      */
16051     setValue : function(v)
16052     {
16053         if(Roo.isIOS && this.useNativeIOS){
16054             this.setIOSValue(v);
16055             return;
16056         }
16057         
16058         if(this.multiple){
16059             this.syncValue();
16060             return;
16061         }
16062         
16063         var text = v;
16064         if(this.valueField){
16065             var r = this.findRecord(this.valueField, v);
16066             if(r){
16067                 text = r.data[this.displayField];
16068             }else if(this.valueNotFoundText !== undefined){
16069                 text = this.valueNotFoundText;
16070             }
16071         }
16072         this.lastSelectionText = text;
16073         if(this.hiddenField){
16074             this.hiddenField.dom.value = v;
16075         }
16076         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16077         this.value = v;
16078         
16079         var close = this.closeTriggerEl();
16080         
16081         if(close){
16082             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16083         }
16084         
16085         this.validate();
16086     },
16087     /**
16088      * @property {Object} the last set data for the element
16089      */
16090     
16091     lastData : false,
16092     /**
16093      * Sets the value of the field based on a object which is related to the record format for the store.
16094      * @param {Object} value the value to set as. or false on reset?
16095      */
16096     setFromData : function(o){
16097         
16098         if(this.multiple){
16099             this.addItem(o);
16100             return;
16101         }
16102             
16103         var dv = ''; // display value
16104         var vv = ''; // value value..
16105         this.lastData = o;
16106         if (this.displayField) {
16107             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16108         } else {
16109             // this is an error condition!!!
16110             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16111         }
16112         
16113         if(this.valueField){
16114             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16115         }
16116         
16117         var close = this.closeTriggerEl();
16118         
16119         if(close){
16120             if(dv.length || vv * 1 > 0){
16121                 close.show() ;
16122                 this.blockFocus=true;
16123             } else {
16124                 close.hide();
16125             }             
16126         }
16127         
16128         if(this.hiddenField){
16129             this.hiddenField.dom.value = vv;
16130             
16131             this.lastSelectionText = dv;
16132             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16133             this.value = vv;
16134             return;
16135         }
16136         // no hidden field.. - we store the value in 'value', but still display
16137         // display field!!!!
16138         this.lastSelectionText = dv;
16139         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16140         this.value = vv;
16141         
16142         
16143         
16144     },
16145     // private
16146     reset : function(){
16147         // overridden so that last data is reset..
16148         
16149         if(this.multiple){
16150             this.clearItem();
16151             return;
16152         }
16153         
16154         this.setValue(this.originalValue);
16155         //this.clearInvalid();
16156         this.lastData = false;
16157         if (this.view) {
16158             this.view.clearSelections();
16159         }
16160         
16161         this.validate();
16162     },
16163     // private
16164     findRecord : function(prop, value){
16165         var record;
16166         if(this.store.getCount() > 0){
16167             this.store.each(function(r){
16168                 if(r.data[prop] == value){
16169                     record = r;
16170                     return false;
16171                 }
16172                 return true;
16173             });
16174         }
16175         return record;
16176     },
16177     
16178     getName: function()
16179     {
16180         // returns hidden if it's set..
16181         if (!this.rendered) {return ''};
16182         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16183         
16184     },
16185     // private
16186     onViewMove : function(e, t){
16187         this.inKeyMode = false;
16188     },
16189
16190     // private
16191     onViewOver : function(e, t){
16192         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16193             return;
16194         }
16195         var item = this.view.findItemFromChild(t);
16196         
16197         if(item){
16198             var index = this.view.indexOf(item);
16199             this.select(index, false);
16200         }
16201     },
16202
16203     // private
16204     onViewClick : function(view, doFocus, el, e)
16205     {
16206         var index = this.view.getSelectedIndexes()[0];
16207         
16208         var r = this.store.getAt(index);
16209         
16210         if(this.tickable){
16211             
16212             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16213                 return;
16214             }
16215             
16216             var rm = false;
16217             var _this = this;
16218             
16219             Roo.each(this.tickItems, function(v,k){
16220                 
16221                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16222                     Roo.log(v);
16223                     _this.tickItems.splice(k, 1);
16224                     
16225                     if(typeof(e) == 'undefined' && view == false){
16226                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16227                     }
16228                     
16229                     rm = true;
16230                     return;
16231                 }
16232             });
16233             
16234             if(rm){
16235                 return;
16236             }
16237             
16238             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16239                 this.tickItems.push(r.data);
16240             }
16241             
16242             if(typeof(e) == 'undefined' && view == false){
16243                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16244             }
16245                     
16246             return;
16247         }
16248         
16249         if(r){
16250             this.onSelect(r, index);
16251         }
16252         if(doFocus !== false && !this.blockFocus){
16253             this.inputEl().focus();
16254         }
16255     },
16256
16257     // private
16258     restrictHeight : function(){
16259         //this.innerList.dom.style.height = '';
16260         //var inner = this.innerList.dom;
16261         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16262         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16263         //this.list.beginUpdate();
16264         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16265         this.list.alignTo(this.inputEl(), this.listAlign);
16266         this.list.alignTo(this.inputEl(), this.listAlign);
16267         //this.list.endUpdate();
16268     },
16269
16270     // private
16271     onEmptyResults : function(){
16272         
16273         if(this.tickable && this.editable){
16274             this.hasFocus = false;
16275             this.restrictHeight();
16276             return;
16277         }
16278         
16279         this.collapse();
16280     },
16281
16282     /**
16283      * Returns true if the dropdown list is expanded, else false.
16284      */
16285     isExpanded : function(){
16286         return this.list.isVisible();
16287     },
16288
16289     /**
16290      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16291      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16292      * @param {String} value The data value of the item to select
16293      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16294      * selected item if it is not currently in view (defaults to true)
16295      * @return {Boolean} True if the value matched an item in the list, else false
16296      */
16297     selectByValue : function(v, scrollIntoView){
16298         if(v !== undefined && v !== null){
16299             var r = this.findRecord(this.valueField || this.displayField, v);
16300             if(r){
16301                 this.select(this.store.indexOf(r), scrollIntoView);
16302                 return true;
16303             }
16304         }
16305         return false;
16306     },
16307
16308     /**
16309      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16310      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16311      * @param {Number} index The zero-based index of the list item to select
16312      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16313      * selected item if it is not currently in view (defaults to true)
16314      */
16315     select : function(index, scrollIntoView){
16316         this.selectedIndex = index;
16317         this.view.select(index);
16318         if(scrollIntoView !== false){
16319             var el = this.view.getNode(index);
16320             /*
16321              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16322              */
16323             if(el){
16324                 this.list.scrollChildIntoView(el, false);
16325             }
16326         }
16327     },
16328
16329     // private
16330     selectNext : function(){
16331         var ct = this.store.getCount();
16332         if(ct > 0){
16333             if(this.selectedIndex == -1){
16334                 this.select(0);
16335             }else if(this.selectedIndex < ct-1){
16336                 this.select(this.selectedIndex+1);
16337             }
16338         }
16339     },
16340
16341     // private
16342     selectPrev : function(){
16343         var ct = this.store.getCount();
16344         if(ct > 0){
16345             if(this.selectedIndex == -1){
16346                 this.select(0);
16347             }else if(this.selectedIndex != 0){
16348                 this.select(this.selectedIndex-1);
16349             }
16350         }
16351     },
16352
16353     // private
16354     onKeyUp : function(e){
16355         if(this.editable !== false && !e.isSpecialKey()){
16356             this.lastKey = e.getKey();
16357             this.dqTask.delay(this.queryDelay);
16358         }
16359     },
16360
16361     // private
16362     validateBlur : function(){
16363         return !this.list || !this.list.isVisible();   
16364     },
16365
16366     // private
16367     initQuery : function(){
16368         
16369         var v = this.getRawValue();
16370         
16371         if(this.tickable && this.editable){
16372             v = this.tickableInputEl().getValue();
16373         }
16374         
16375         this.doQuery(v);
16376     },
16377
16378     // private
16379     doForce : function(){
16380         if(this.inputEl().dom.value.length > 0){
16381             this.inputEl().dom.value =
16382                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16383              
16384         }
16385     },
16386
16387     /**
16388      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16389      * query allowing the query action to be canceled if needed.
16390      * @param {String} query The SQL query to execute
16391      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16392      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16393      * saved in the current store (defaults to false)
16394      */
16395     doQuery : function(q, forceAll){
16396         
16397         if(q === undefined || q === null){
16398             q = '';
16399         }
16400         var qe = {
16401             query: q,
16402             forceAll: forceAll,
16403             combo: this,
16404             cancel:false
16405         };
16406         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16407             return false;
16408         }
16409         q = qe.query;
16410         
16411         forceAll = qe.forceAll;
16412         if(forceAll === true || (q.length >= this.minChars)){
16413             
16414             this.hasQuery = true;
16415             
16416             if(this.lastQuery != q || this.alwaysQuery){
16417                 this.lastQuery = q;
16418                 if(this.mode == 'local'){
16419                     this.selectedIndex = -1;
16420                     if(forceAll){
16421                         this.store.clearFilter();
16422                     }else{
16423                         
16424                         if(this.specialFilter){
16425                             this.fireEvent('specialfilter', this);
16426                             this.onLoad();
16427                             return;
16428                         }
16429                         
16430                         this.store.filter(this.displayField, q);
16431                     }
16432                     
16433                     this.store.fireEvent("datachanged", this.store);
16434                     
16435                     this.onLoad();
16436                     
16437                     
16438                 }else{
16439                     
16440                     this.store.baseParams[this.queryParam] = q;
16441                     
16442                     var options = {params : this.getParams(q)};
16443                     
16444                     if(this.loadNext){
16445                         options.add = true;
16446                         options.params.start = this.page * this.pageSize;
16447                     }
16448                     
16449                     this.store.load(options);
16450                     
16451                     /*
16452                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16453                      *  we should expand the list on onLoad
16454                      *  so command out it
16455                      */
16456 //                    this.expand();
16457                 }
16458             }else{
16459                 this.selectedIndex = -1;
16460                 this.onLoad();   
16461             }
16462         }
16463         
16464         this.loadNext = false;
16465     },
16466     
16467     // private
16468     getParams : function(q){
16469         var p = {};
16470         //p[this.queryParam] = q;
16471         
16472         if(this.pageSize){
16473             p.start = 0;
16474             p.limit = this.pageSize;
16475         }
16476         return p;
16477     },
16478
16479     /**
16480      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16481      */
16482     collapse : function(){
16483         if(!this.isExpanded()){
16484             return;
16485         }
16486         
16487         this.list.hide();
16488         
16489         this.hasFocus = false;
16490         
16491         if(this.tickable){
16492             this.okBtn.hide();
16493             this.cancelBtn.hide();
16494             this.trigger.show();
16495             
16496             if(this.editable){
16497                 this.tickableInputEl().dom.value = '';
16498                 this.tickableInputEl().blur();
16499             }
16500             
16501         }
16502         
16503         Roo.get(document).un('mousedown', this.collapseIf, this);
16504         Roo.get(document).un('mousewheel', this.collapseIf, this);
16505         if (!this.editable) {
16506             Roo.get(document).un('keydown', this.listKeyPress, this);
16507         }
16508         this.fireEvent('collapse', this);
16509         
16510         this.validate();
16511     },
16512
16513     // private
16514     collapseIf : function(e){
16515         var in_combo  = e.within(this.el);
16516         var in_list =  e.within(this.list);
16517         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16518         
16519         if (in_combo || in_list || is_list) {
16520             //e.stopPropagation();
16521             return;
16522         }
16523         
16524         if(this.tickable){
16525             this.onTickableFooterButtonClick(e, false, false);
16526         }
16527
16528         this.collapse();
16529         
16530     },
16531
16532     /**
16533      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16534      */
16535     expand : function(){
16536        
16537         if(this.isExpanded() || !this.hasFocus){
16538             return;
16539         }
16540         
16541         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16542         this.list.setWidth(lw);
16543         
16544         Roo.log('expand');
16545         
16546         this.list.show();
16547         
16548         this.restrictHeight();
16549         
16550         if(this.tickable){
16551             
16552             this.tickItems = Roo.apply([], this.item);
16553             
16554             this.okBtn.show();
16555             this.cancelBtn.show();
16556             this.trigger.hide();
16557             
16558             if(this.editable){
16559                 this.tickableInputEl().focus();
16560             }
16561             
16562         }
16563         
16564         Roo.get(document).on('mousedown', this.collapseIf, this);
16565         Roo.get(document).on('mousewheel', this.collapseIf, this);
16566         if (!this.editable) {
16567             Roo.get(document).on('keydown', this.listKeyPress, this);
16568         }
16569         
16570         this.fireEvent('expand', this);
16571     },
16572
16573     // private
16574     // Implements the default empty TriggerField.onTriggerClick function
16575     onTriggerClick : function(e)
16576     {
16577         Roo.log('trigger click');
16578         
16579         if(this.disabled || !this.triggerList){
16580             return;
16581         }
16582         
16583         this.page = 0;
16584         this.loadNext = false;
16585         
16586         if(this.isExpanded()){
16587             this.collapse();
16588             if (!this.blockFocus) {
16589                 this.inputEl().focus();
16590             }
16591             
16592         }else {
16593             this.hasFocus = true;
16594             if(this.triggerAction == 'all') {
16595                 this.doQuery(this.allQuery, true);
16596             } else {
16597                 this.doQuery(this.getRawValue());
16598             }
16599             if (!this.blockFocus) {
16600                 this.inputEl().focus();
16601             }
16602         }
16603     },
16604     
16605     onTickableTriggerClick : function(e)
16606     {
16607         if(this.disabled){
16608             return;
16609         }
16610         
16611         this.page = 0;
16612         this.loadNext = false;
16613         this.hasFocus = true;
16614         
16615         if(this.triggerAction == 'all') {
16616             this.doQuery(this.allQuery, true);
16617         } else {
16618             this.doQuery(this.getRawValue());
16619         }
16620     },
16621     
16622     onSearchFieldClick : function(e)
16623     {
16624         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16625             this.onTickableFooterButtonClick(e, false, false);
16626             return;
16627         }
16628         
16629         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16630             return;
16631         }
16632         
16633         this.page = 0;
16634         this.loadNext = false;
16635         this.hasFocus = true;
16636         
16637         if(this.triggerAction == 'all') {
16638             this.doQuery(this.allQuery, true);
16639         } else {
16640             this.doQuery(this.getRawValue());
16641         }
16642     },
16643     
16644     listKeyPress : function(e)
16645     {
16646         //Roo.log('listkeypress');
16647         // scroll to first matching element based on key pres..
16648         if (e.isSpecialKey()) {
16649             return false;
16650         }
16651         var k = String.fromCharCode(e.getKey()).toUpperCase();
16652         //Roo.log(k);
16653         var match  = false;
16654         var csel = this.view.getSelectedNodes();
16655         var cselitem = false;
16656         if (csel.length) {
16657             var ix = this.view.indexOf(csel[0]);
16658             cselitem  = this.store.getAt(ix);
16659             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16660                 cselitem = false;
16661             }
16662             
16663         }
16664         
16665         this.store.each(function(v) { 
16666             if (cselitem) {
16667                 // start at existing selection.
16668                 if (cselitem.id == v.id) {
16669                     cselitem = false;
16670                 }
16671                 return true;
16672             }
16673                 
16674             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16675                 match = this.store.indexOf(v);
16676                 return false;
16677             }
16678             return true;
16679         }, this);
16680         
16681         if (match === false) {
16682             return true; // no more action?
16683         }
16684         // scroll to?
16685         this.view.select(match);
16686         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16687         sn.scrollIntoView(sn.dom.parentNode, false);
16688     },
16689     
16690     onViewScroll : function(e, t){
16691         
16692         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){
16693             return;
16694         }
16695         
16696         this.hasQuery = true;
16697         
16698         this.loading = this.list.select('.loading', true).first();
16699         
16700         if(this.loading === null){
16701             this.list.createChild({
16702                 tag: 'div',
16703                 cls: 'loading roo-select2-more-results roo-select2-active',
16704                 html: 'Loading more results...'
16705             });
16706             
16707             this.loading = this.list.select('.loading', true).first();
16708             
16709             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16710             
16711             this.loading.hide();
16712         }
16713         
16714         this.loading.show();
16715         
16716         var _combo = this;
16717         
16718         this.page++;
16719         this.loadNext = true;
16720         
16721         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16722         
16723         return;
16724     },
16725     
16726     addItem : function(o)
16727     {   
16728         var dv = ''; // display value
16729         
16730         if (this.displayField) {
16731             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16732         } else {
16733             // this is an error condition!!!
16734             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16735         }
16736         
16737         if(!dv.length){
16738             return;
16739         }
16740         
16741         var choice = this.choices.createChild({
16742             tag: 'li',
16743             cls: 'roo-select2-search-choice',
16744             cn: [
16745                 {
16746                     tag: 'div',
16747                     html: dv
16748                 },
16749                 {
16750                     tag: 'a',
16751                     href: '#',
16752                     cls: 'roo-select2-search-choice-close fa fa-times',
16753                     tabindex: '-1'
16754                 }
16755             ]
16756             
16757         }, this.searchField);
16758         
16759         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16760         
16761         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16762         
16763         this.item.push(o);
16764         
16765         this.lastData = o;
16766         
16767         this.syncValue();
16768         
16769         this.inputEl().dom.value = '';
16770         
16771         this.validate();
16772     },
16773     
16774     onRemoveItem : function(e, _self, o)
16775     {
16776         e.preventDefault();
16777         
16778         this.lastItem = Roo.apply([], this.item);
16779         
16780         var index = this.item.indexOf(o.data) * 1;
16781         
16782         if( index < 0){
16783             Roo.log('not this item?!');
16784             return;
16785         }
16786         
16787         this.item.splice(index, 1);
16788         o.item.remove();
16789         
16790         this.syncValue();
16791         
16792         this.fireEvent('remove', this, e);
16793         
16794         this.validate();
16795         
16796     },
16797     
16798     syncValue : function()
16799     {
16800         if(!this.item.length){
16801             this.clearValue();
16802             return;
16803         }
16804             
16805         var value = [];
16806         var _this = this;
16807         Roo.each(this.item, function(i){
16808             if(_this.valueField){
16809                 value.push(i[_this.valueField]);
16810                 return;
16811             }
16812
16813             value.push(i);
16814         });
16815
16816         this.value = value.join(',');
16817
16818         if(this.hiddenField){
16819             this.hiddenField.dom.value = this.value;
16820         }
16821         
16822         this.store.fireEvent("datachanged", this.store);
16823         
16824         this.validate();
16825     },
16826     
16827     clearItem : function()
16828     {
16829         if(!this.multiple){
16830             return;
16831         }
16832         
16833         this.item = [];
16834         
16835         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16836            c.remove();
16837         });
16838         
16839         this.syncValue();
16840         
16841         this.validate();
16842         
16843         if(this.tickable && !Roo.isTouch){
16844             this.view.refresh();
16845         }
16846     },
16847     
16848     inputEl: function ()
16849     {
16850         if(Roo.isIOS && this.useNativeIOS){
16851             return this.el.select('select.roo-ios-select', true).first();
16852         }
16853         
16854         if(Roo.isTouch && this.mobileTouchView){
16855             return this.el.select('input.form-control',true).first();
16856         }
16857         
16858         if(this.tickable){
16859             return this.searchField;
16860         }
16861         
16862         return this.el.select('input.form-control',true).first();
16863     },
16864     
16865     onTickableFooterButtonClick : function(e, btn, el)
16866     {
16867         e.preventDefault();
16868         
16869         this.lastItem = Roo.apply([], this.item);
16870         
16871         if(btn && btn.name == 'cancel'){
16872             this.tickItems = Roo.apply([], this.item);
16873             this.collapse();
16874             return;
16875         }
16876         
16877         this.clearItem();
16878         
16879         var _this = this;
16880         
16881         Roo.each(this.tickItems, function(o){
16882             _this.addItem(o);
16883         });
16884         
16885         this.collapse();
16886         
16887     },
16888     
16889     validate : function()
16890     {
16891         if(this.getVisibilityEl().hasClass('hidden')){
16892             return true;
16893         }
16894         
16895         var v = this.getRawValue();
16896         
16897         if(this.multiple){
16898             v = this.getValue();
16899         }
16900         
16901         if(this.disabled || this.allowBlank || v.length){
16902             this.markValid();
16903             return true;
16904         }
16905         
16906         this.markInvalid();
16907         return false;
16908     },
16909     
16910     tickableInputEl : function()
16911     {
16912         if(!this.tickable || !this.editable){
16913             return this.inputEl();
16914         }
16915         
16916         return this.inputEl().select('.roo-select2-search-field-input', true).first();
16917     },
16918     
16919     
16920     getAutoCreateTouchView : function()
16921     {
16922         var id = Roo.id();
16923         
16924         var cfg = {
16925             cls: 'form-group' //input-group
16926         };
16927         
16928         var input =  {
16929             tag: 'input',
16930             id : id,
16931             type : this.inputType,
16932             cls : 'form-control x-combo-noedit',
16933             autocomplete: 'new-password',
16934             placeholder : this.placeholder || '',
16935             readonly : true
16936         };
16937         
16938         if (this.name) {
16939             input.name = this.name;
16940         }
16941         
16942         if (this.size) {
16943             input.cls += ' input-' + this.size;
16944         }
16945         
16946         if (this.disabled) {
16947             input.disabled = true;
16948         }
16949         
16950         var inputblock = {
16951             cls : 'roo-combobox-wrap',
16952             cn : [
16953                 input
16954             ]
16955         };
16956         
16957         if(this.before){
16958             inputblock.cls += ' input-group';
16959             
16960             inputblock.cn.unshift({
16961                 tag :'span',
16962                 cls : 'input-group-addon input-group-prepend input-group-text',
16963                 html : this.before
16964             });
16965         }
16966         
16967         if(this.removable && !this.multiple){
16968             inputblock.cls += ' roo-removable';
16969             
16970             inputblock.cn.push({
16971                 tag: 'button',
16972                 html : 'x',
16973                 cls : 'roo-combo-removable-btn close'
16974             });
16975         }
16976
16977         if(this.hasFeedback && !this.allowBlank){
16978             
16979             inputblock.cls += ' has-feedback';
16980             
16981             inputblock.cn.push({
16982                 tag: 'span',
16983                 cls: 'glyphicon form-control-feedback'
16984             });
16985             
16986         }
16987         
16988         if (this.after) {
16989             
16990             inputblock.cls += (this.before) ? '' : ' input-group';
16991             
16992             inputblock.cn.push({
16993                 tag :'span',
16994                 cls : 'input-group-addon input-group-append input-group-text',
16995                 html : this.after
16996             });
16997         }
16998
16999         
17000         var ibwrap = inputblock;
17001         
17002         if(this.multiple){
17003             ibwrap = {
17004                 tag: 'ul',
17005                 cls: 'roo-select2-choices',
17006                 cn:[
17007                     {
17008                         tag: 'li',
17009                         cls: 'roo-select2-search-field',
17010                         cn: [
17011
17012                             inputblock
17013                         ]
17014                     }
17015                 ]
17016             };
17017         
17018             
17019         }
17020         
17021         var combobox = {
17022             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17023             cn: [
17024                 {
17025                     tag: 'input',
17026                     type : 'hidden',
17027                     cls: 'form-hidden-field'
17028                 },
17029                 ibwrap
17030             ]
17031         };
17032         
17033         if(!this.multiple && this.showToggleBtn){
17034             
17035             var caret = {
17036                 cls: 'caret'
17037             };
17038             
17039             if (this.caret != false) {
17040                 caret = {
17041                      tag: 'i',
17042                      cls: 'fa fa-' + this.caret
17043                 };
17044                 
17045             }
17046             
17047             combobox.cn.push({
17048                 tag :'span',
17049                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17050                 cn : [
17051                     Roo.bootstrap.version == 3 ? caret : '',
17052                     {
17053                         tag: 'span',
17054                         cls: 'combobox-clear',
17055                         cn  : [
17056                             {
17057                                 tag : 'i',
17058                                 cls: 'icon-remove'
17059                             }
17060                         ]
17061                     }
17062                 ]
17063
17064             })
17065         }
17066         
17067         if(this.multiple){
17068             combobox.cls += ' roo-select2-container-multi';
17069         }
17070         
17071         var align = this.labelAlign || this.parentLabelAlign();
17072         
17073         if (align ==='left' && this.fieldLabel.length) {
17074
17075             cfg.cn = [
17076                 {
17077                    tag : 'i',
17078                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17079                    tooltip : 'This field is required'
17080                 },
17081                 {
17082                     tag: 'label',
17083                     cls : 'control-label col-form-label',
17084                     html : this.fieldLabel
17085
17086                 },
17087                 {
17088                     cls : 'roo-combobox-wrap ', 
17089                     cn: [
17090                         combobox
17091                     ]
17092                 }
17093             ];
17094             
17095             var labelCfg = cfg.cn[1];
17096             var contentCfg = cfg.cn[2];
17097             
17098
17099             if(this.indicatorpos == 'right'){
17100                 cfg.cn = [
17101                     {
17102                         tag: 'label',
17103                         'for' :  id,
17104                         cls : 'control-label col-form-label',
17105                         cn : [
17106                             {
17107                                 tag : 'span',
17108                                 html : this.fieldLabel
17109                             },
17110                             {
17111                                 tag : 'i',
17112                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17113                                 tooltip : 'This field is required'
17114                             }
17115                         ]
17116                     },
17117                     {
17118                         cls : "roo-combobox-wrap ",
17119                         cn: [
17120                             combobox
17121                         ]
17122                     }
17123
17124                 ];
17125                 
17126                 labelCfg = cfg.cn[0];
17127                 contentCfg = cfg.cn[1];
17128             }
17129             
17130            
17131             
17132             if(this.labelWidth > 12){
17133                 labelCfg.style = "width: " + this.labelWidth + 'px';
17134             }
17135            
17136             if(this.labelWidth < 13 && this.labelmd == 0){
17137                 this.labelmd = this.labelWidth;
17138             }
17139             
17140             if(this.labellg > 0){
17141                 labelCfg.cls += ' col-lg-' + this.labellg;
17142                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17143             }
17144             
17145             if(this.labelmd > 0){
17146                 labelCfg.cls += ' col-md-' + this.labelmd;
17147                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17148             }
17149             
17150             if(this.labelsm > 0){
17151                 labelCfg.cls += ' col-sm-' + this.labelsm;
17152                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17153             }
17154             
17155             if(this.labelxs > 0){
17156                 labelCfg.cls += ' col-xs-' + this.labelxs;
17157                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17158             }
17159                 
17160                 
17161         } else if ( this.fieldLabel.length) {
17162             cfg.cn = [
17163                 {
17164                    tag : 'i',
17165                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17166                    tooltip : 'This field is required'
17167                 },
17168                 {
17169                     tag: 'label',
17170                     cls : 'control-label',
17171                     html : this.fieldLabel
17172
17173                 },
17174                 {
17175                     cls : '', 
17176                     cn: [
17177                         combobox
17178                     ]
17179                 }
17180             ];
17181             
17182             if(this.indicatorpos == 'right'){
17183                 cfg.cn = [
17184                     {
17185                         tag: 'label',
17186                         cls : 'control-label',
17187                         html : this.fieldLabel,
17188                         cn : [
17189                             {
17190                                tag : 'i',
17191                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17192                                tooltip : 'This field is required'
17193                             }
17194                         ]
17195                     },
17196                     {
17197                         cls : '', 
17198                         cn: [
17199                             combobox
17200                         ]
17201                     }
17202                 ];
17203             }
17204         } else {
17205             cfg.cn = combobox;    
17206         }
17207         
17208         
17209         var settings = this;
17210         
17211         ['xs','sm','md','lg'].map(function(size){
17212             if (settings[size]) {
17213                 cfg.cls += ' col-' + size + '-' + settings[size];
17214             }
17215         });
17216         
17217         return cfg;
17218     },
17219     
17220     initTouchView : function()
17221     {
17222         this.renderTouchView();
17223         
17224         this.touchViewEl.on('scroll', function(){
17225             this.el.dom.scrollTop = 0;
17226         }, this);
17227         
17228         this.originalValue = this.getValue();
17229         
17230         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17231         
17232         this.inputEl().on("click", this.showTouchView, this);
17233         if (this.triggerEl) {
17234             this.triggerEl.on("click", this.showTouchView, this);
17235         }
17236         
17237         
17238         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17239         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17240         
17241         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17242         
17243         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17244         this.store.on('load', this.onTouchViewLoad, this);
17245         this.store.on('loadexception', this.onTouchViewLoadException, this);
17246         
17247         if(this.hiddenName){
17248             
17249             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17250             
17251             this.hiddenField.dom.value =
17252                 this.hiddenValue !== undefined ? this.hiddenValue :
17253                 this.value !== undefined ? this.value : '';
17254         
17255             this.el.dom.removeAttribute('name');
17256             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17257         }
17258         
17259         if(this.multiple){
17260             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17261             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17262         }
17263         
17264         if(this.removable && !this.multiple){
17265             var close = this.closeTriggerEl();
17266             if(close){
17267                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17268                 close.on('click', this.removeBtnClick, this, close);
17269             }
17270         }
17271         /*
17272          * fix the bug in Safari iOS8
17273          */
17274         this.inputEl().on("focus", function(e){
17275             document.activeElement.blur();
17276         }, this);
17277         
17278         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17279         
17280         return;
17281         
17282         
17283     },
17284     
17285     renderTouchView : function()
17286     {
17287         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17288         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17289         
17290         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17291         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17292         
17293         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17294         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17295         this.touchViewBodyEl.setStyle('overflow', 'auto');
17296         
17297         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17298         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17299         
17300         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17301         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17302         
17303     },
17304     
17305     showTouchView : function()
17306     {
17307         if(this.disabled){
17308             return;
17309         }
17310         
17311         this.touchViewHeaderEl.hide();
17312
17313         if(this.modalTitle.length){
17314             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17315             this.touchViewHeaderEl.show();
17316         }
17317
17318         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17319         this.touchViewEl.show();
17320
17321         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17322         
17323         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17324         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17325
17326         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17327
17328         if(this.modalTitle.length){
17329             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17330         }
17331         
17332         this.touchViewBodyEl.setHeight(bodyHeight);
17333
17334         if(this.animate){
17335             var _this = this;
17336             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17337         }else{
17338             this.touchViewEl.addClass(['in','show']);
17339         }
17340         
17341         if(this._touchViewMask){
17342             Roo.get(document.body).addClass("x-body-masked");
17343             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17344             this._touchViewMask.setStyle('z-index', 10000);
17345             this._touchViewMask.addClass('show');
17346         }
17347         
17348         this.doTouchViewQuery();
17349         
17350     },
17351     
17352     hideTouchView : function()
17353     {
17354         this.touchViewEl.removeClass(['in','show']);
17355
17356         if(this.animate){
17357             var _this = this;
17358             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17359         }else{
17360             this.touchViewEl.setStyle('display', 'none');
17361         }
17362         
17363         if(this._touchViewMask){
17364             this._touchViewMask.removeClass('show');
17365             Roo.get(document.body).removeClass("x-body-masked");
17366         }
17367     },
17368     
17369     setTouchViewValue : function()
17370     {
17371         if(this.multiple){
17372             this.clearItem();
17373         
17374             var _this = this;
17375
17376             Roo.each(this.tickItems, function(o){
17377                 this.addItem(o);
17378             }, this);
17379         }
17380         
17381         this.hideTouchView();
17382     },
17383     
17384     doTouchViewQuery : function()
17385     {
17386         var qe = {
17387             query: '',
17388             forceAll: true,
17389             combo: this,
17390             cancel:false
17391         };
17392         
17393         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17394             return false;
17395         }
17396         
17397         if(!this.alwaysQuery || this.mode == 'local'){
17398             this.onTouchViewLoad();
17399             return;
17400         }
17401         
17402         this.store.load();
17403     },
17404     
17405     onTouchViewBeforeLoad : function(combo,opts)
17406     {
17407         return;
17408     },
17409
17410     // private
17411     onTouchViewLoad : function()
17412     {
17413         if(this.store.getCount() < 1){
17414             this.onTouchViewEmptyResults();
17415             return;
17416         }
17417         
17418         this.clearTouchView();
17419         
17420         var rawValue = this.getRawValue();
17421         
17422         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17423         
17424         this.tickItems = [];
17425         
17426         this.store.data.each(function(d, rowIndex){
17427             var row = this.touchViewListGroup.createChild(template);
17428             
17429             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17430                 row.addClass(d.data.cls);
17431             }
17432             
17433             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17434                 var cfg = {
17435                     data : d.data,
17436                     html : d.data[this.displayField]
17437                 };
17438                 
17439                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17440                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17441                 }
17442             }
17443             row.removeClass('selected');
17444             if(!this.multiple && this.valueField &&
17445                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17446             {
17447                 // radio buttons..
17448                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17449                 row.addClass('selected');
17450             }
17451             
17452             if(this.multiple && this.valueField &&
17453                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17454             {
17455                 
17456                 // checkboxes...
17457                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17458                 this.tickItems.push(d.data);
17459             }
17460             
17461             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17462             
17463         }, this);
17464         
17465         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17466         
17467         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17468
17469         if(this.modalTitle.length){
17470             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17471         }
17472
17473         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17474         
17475         if(this.mobile_restrict_height && listHeight < bodyHeight){
17476             this.touchViewBodyEl.setHeight(listHeight);
17477         }
17478         
17479         var _this = this;
17480         
17481         if(firstChecked && listHeight > bodyHeight){
17482             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17483         }
17484         
17485     },
17486     
17487     onTouchViewLoadException : function()
17488     {
17489         this.hideTouchView();
17490     },
17491     
17492     onTouchViewEmptyResults : function()
17493     {
17494         this.clearTouchView();
17495         
17496         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17497         
17498         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17499         
17500     },
17501     
17502     clearTouchView : function()
17503     {
17504         this.touchViewListGroup.dom.innerHTML = '';
17505     },
17506     
17507     onTouchViewClick : function(e, el, o)
17508     {
17509         e.preventDefault();
17510         
17511         var row = o.row;
17512         var rowIndex = o.rowIndex;
17513         
17514         var r = this.store.getAt(rowIndex);
17515         
17516         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17517             
17518             if(!this.multiple){
17519                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17520                     c.dom.removeAttribute('checked');
17521                 }, this);
17522
17523                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17524
17525                 this.setFromData(r.data);
17526
17527                 var close = this.closeTriggerEl();
17528
17529                 if(close){
17530                     close.show();
17531                 }
17532
17533                 this.hideTouchView();
17534
17535                 this.fireEvent('select', this, r, rowIndex);
17536
17537                 return;
17538             }
17539
17540             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17541                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17542                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17543                 return;
17544             }
17545
17546             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17547             this.addItem(r.data);
17548             this.tickItems.push(r.data);
17549         }
17550     },
17551     
17552     getAutoCreateNativeIOS : function()
17553     {
17554         var cfg = {
17555             cls: 'form-group' //input-group,
17556         };
17557         
17558         var combobox =  {
17559             tag: 'select',
17560             cls : 'roo-ios-select'
17561         };
17562         
17563         if (this.name) {
17564             combobox.name = this.name;
17565         }
17566         
17567         if (this.disabled) {
17568             combobox.disabled = true;
17569         }
17570         
17571         var settings = this;
17572         
17573         ['xs','sm','md','lg'].map(function(size){
17574             if (settings[size]) {
17575                 cfg.cls += ' col-' + size + '-' + settings[size];
17576             }
17577         });
17578         
17579         cfg.cn = combobox;
17580         
17581         return cfg;
17582         
17583     },
17584     
17585     initIOSView : function()
17586     {
17587         this.store.on('load', this.onIOSViewLoad, this);
17588         
17589         return;
17590     },
17591     
17592     onIOSViewLoad : function()
17593     {
17594         if(this.store.getCount() < 1){
17595             return;
17596         }
17597         
17598         this.clearIOSView();
17599         
17600         if(this.allowBlank) {
17601             
17602             var default_text = '-- SELECT --';
17603             
17604             if(this.placeholder.length){
17605                 default_text = this.placeholder;
17606             }
17607             
17608             if(this.emptyTitle.length){
17609                 default_text += ' - ' + this.emptyTitle + ' -';
17610             }
17611             
17612             var opt = this.inputEl().createChild({
17613                 tag: 'option',
17614                 value : 0,
17615                 html : default_text
17616             });
17617             
17618             var o = {};
17619             o[this.valueField] = 0;
17620             o[this.displayField] = default_text;
17621             
17622             this.ios_options.push({
17623                 data : o,
17624                 el : opt
17625             });
17626             
17627         }
17628         
17629         this.store.data.each(function(d, rowIndex){
17630             
17631             var html = '';
17632             
17633             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17634                 html = d.data[this.displayField];
17635             }
17636             
17637             var value = '';
17638             
17639             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17640                 value = d.data[this.valueField];
17641             }
17642             
17643             var option = {
17644                 tag: 'option',
17645                 value : value,
17646                 html : html
17647             };
17648             
17649             if(this.value == d.data[this.valueField]){
17650                 option['selected'] = true;
17651             }
17652             
17653             var opt = this.inputEl().createChild(option);
17654             
17655             this.ios_options.push({
17656                 data : d.data,
17657                 el : opt
17658             });
17659             
17660         }, this);
17661         
17662         this.inputEl().on('change', function(){
17663            this.fireEvent('select', this);
17664         }, this);
17665         
17666     },
17667     
17668     clearIOSView: function()
17669     {
17670         this.inputEl().dom.innerHTML = '';
17671         
17672         this.ios_options = [];
17673     },
17674     
17675     setIOSValue: function(v)
17676     {
17677         this.value = v;
17678         
17679         if(!this.ios_options){
17680             return;
17681         }
17682         
17683         Roo.each(this.ios_options, function(opts){
17684            
17685            opts.el.dom.removeAttribute('selected');
17686            
17687            if(opts.data[this.valueField] != v){
17688                return;
17689            }
17690            
17691            opts.el.dom.setAttribute('selected', true);
17692            
17693         }, this);
17694     }
17695
17696     /** 
17697     * @cfg {Boolean} grow 
17698     * @hide 
17699     */
17700     /** 
17701     * @cfg {Number} growMin 
17702     * @hide 
17703     */
17704     /** 
17705     * @cfg {Number} growMax 
17706     * @hide 
17707     */
17708     /**
17709      * @hide
17710      * @method autoSize
17711      */
17712 });
17713
17714 Roo.apply(Roo.bootstrap.ComboBox,  {
17715     
17716     header : {
17717         tag: 'div',
17718         cls: 'modal-header',
17719         cn: [
17720             {
17721                 tag: 'h4',
17722                 cls: 'modal-title'
17723             }
17724         ]
17725     },
17726     
17727     body : {
17728         tag: 'div',
17729         cls: 'modal-body',
17730         cn: [
17731             {
17732                 tag: 'ul',
17733                 cls: 'list-group'
17734             }
17735         ]
17736     },
17737     
17738     listItemRadio : {
17739         tag: 'li',
17740         cls: 'list-group-item',
17741         cn: [
17742             {
17743                 tag: 'span',
17744                 cls: 'roo-combobox-list-group-item-value'
17745             },
17746             {
17747                 tag: 'div',
17748                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17749                 cn: [
17750                     {
17751                         tag: 'input',
17752                         type: 'radio'
17753                     },
17754                     {
17755                         tag: 'label'
17756                     }
17757                 ]
17758             }
17759         ]
17760     },
17761     
17762     listItemCheckbox : {
17763         tag: 'li',
17764         cls: 'list-group-item',
17765         cn: [
17766             {
17767                 tag: 'span',
17768                 cls: 'roo-combobox-list-group-item-value'
17769             },
17770             {
17771                 tag: 'div',
17772                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17773                 cn: [
17774                     {
17775                         tag: 'input',
17776                         type: 'checkbox'
17777                     },
17778                     {
17779                         tag: 'label'
17780                     }
17781                 ]
17782             }
17783         ]
17784     },
17785     
17786     emptyResult : {
17787         tag: 'div',
17788         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17789     },
17790     
17791     footer : {
17792         tag: 'div',
17793         cls: 'modal-footer',
17794         cn: [
17795             {
17796                 tag: 'div',
17797                 cls: 'row',
17798                 cn: [
17799                     {
17800                         tag: 'div',
17801                         cls: 'col-xs-6 text-left',
17802                         cn: {
17803                             tag: 'button',
17804                             cls: 'btn btn-danger roo-touch-view-cancel',
17805                             html: 'Cancel'
17806                         }
17807                     },
17808                     {
17809                         tag: 'div',
17810                         cls: 'col-xs-6 text-right',
17811                         cn: {
17812                             tag: 'button',
17813                             cls: 'btn btn-success roo-touch-view-ok',
17814                             html: 'OK'
17815                         }
17816                     }
17817                 ]
17818             }
17819         ]
17820         
17821     }
17822 });
17823
17824 Roo.apply(Roo.bootstrap.ComboBox,  {
17825     
17826     touchViewTemplate : {
17827         tag: 'div',
17828         cls: 'modal fade roo-combobox-touch-view',
17829         cn: [
17830             {
17831                 tag: 'div',
17832                 cls: 'modal-dialog',
17833                 style : 'position:fixed', // we have to fix position....
17834                 cn: [
17835                     {
17836                         tag: 'div',
17837                         cls: 'modal-content',
17838                         cn: [
17839                             Roo.bootstrap.ComboBox.header,
17840                             Roo.bootstrap.ComboBox.body,
17841                             Roo.bootstrap.ComboBox.footer
17842                         ]
17843                     }
17844                 ]
17845             }
17846         ]
17847     }
17848 });/*
17849  * Based on:
17850  * Ext JS Library 1.1.1
17851  * Copyright(c) 2006-2007, Ext JS, LLC.
17852  *
17853  * Originally Released Under LGPL - original licence link has changed is not relivant.
17854  *
17855  * Fork - LGPL
17856  * <script type="text/javascript">
17857  */
17858
17859 /**
17860  * @class Roo.View
17861  * @extends Roo.util.Observable
17862  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17863  * This class also supports single and multi selection modes. <br>
17864  * Create a data model bound view:
17865  <pre><code>
17866  var store = new Roo.data.Store(...);
17867
17868  var view = new Roo.View({
17869     el : "my-element",
17870     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
17871  
17872     singleSelect: true,
17873     selectedClass: "ydataview-selected",
17874     store: store
17875  });
17876
17877  // listen for node click?
17878  view.on("click", function(vw, index, node, e){
17879  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17880  });
17881
17882  // load XML data
17883  dataModel.load("foobar.xml");
17884  </code></pre>
17885  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17886  * <br><br>
17887  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17888  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17889  * 
17890  * Note: old style constructor is still suported (container, template, config)
17891  * 
17892  * @constructor
17893  * Create a new View
17894  * @param {Object} config The config object
17895  * 
17896  */
17897 Roo.View = function(config, depreciated_tpl, depreciated_config){
17898     
17899     this.parent = false;
17900     
17901     if (typeof(depreciated_tpl) == 'undefined') {
17902         // new way.. - universal constructor.
17903         Roo.apply(this, config);
17904         this.el  = Roo.get(this.el);
17905     } else {
17906         // old format..
17907         this.el  = Roo.get(config);
17908         this.tpl = depreciated_tpl;
17909         Roo.apply(this, depreciated_config);
17910     }
17911     this.wrapEl  = this.el.wrap().wrap();
17912     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17913     
17914     
17915     if(typeof(this.tpl) == "string"){
17916         this.tpl = new Roo.Template(this.tpl);
17917     } else {
17918         // support xtype ctors..
17919         this.tpl = new Roo.factory(this.tpl, Roo);
17920     }
17921     
17922     
17923     this.tpl.compile();
17924     
17925     /** @private */
17926     this.addEvents({
17927         /**
17928          * @event beforeclick
17929          * Fires before a click is processed. Returns false to cancel the default action.
17930          * @param {Roo.View} this
17931          * @param {Number} index The index of the target node
17932          * @param {HTMLElement} node The target node
17933          * @param {Roo.EventObject} e The raw event object
17934          */
17935             "beforeclick" : true,
17936         /**
17937          * @event click
17938          * Fires when a template node is clicked.
17939          * @param {Roo.View} this
17940          * @param {Number} index The index of the target node
17941          * @param {HTMLElement} node The target node
17942          * @param {Roo.EventObject} e The raw event object
17943          */
17944             "click" : true,
17945         /**
17946          * @event dblclick
17947          * Fires when a template node is double clicked.
17948          * @param {Roo.View} this
17949          * @param {Number} index The index of the target node
17950          * @param {HTMLElement} node The target node
17951          * @param {Roo.EventObject} e The raw event object
17952          */
17953             "dblclick" : true,
17954         /**
17955          * @event contextmenu
17956          * Fires when a template node is right clicked.
17957          * @param {Roo.View} this
17958          * @param {Number} index The index of the target node
17959          * @param {HTMLElement} node The target node
17960          * @param {Roo.EventObject} e The raw event object
17961          */
17962             "contextmenu" : true,
17963         /**
17964          * @event selectionchange
17965          * Fires when the selected nodes change.
17966          * @param {Roo.View} this
17967          * @param {Array} selections Array of the selected nodes
17968          */
17969             "selectionchange" : true,
17970     
17971         /**
17972          * @event beforeselect
17973          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17974          * @param {Roo.View} this
17975          * @param {HTMLElement} node The node to be selected
17976          * @param {Array} selections Array of currently selected nodes
17977          */
17978             "beforeselect" : true,
17979         /**
17980          * @event preparedata
17981          * Fires on every row to render, to allow you to change the data.
17982          * @param {Roo.View} this
17983          * @param {Object} data to be rendered (change this)
17984          */
17985           "preparedata" : true
17986           
17987           
17988         });
17989
17990
17991
17992     this.el.on({
17993         "click": this.onClick,
17994         "dblclick": this.onDblClick,
17995         "contextmenu": this.onContextMenu,
17996         scope:this
17997     });
17998
17999     this.selections = [];
18000     this.nodes = [];
18001     this.cmp = new Roo.CompositeElementLite([]);
18002     if(this.store){
18003         this.store = Roo.factory(this.store, Roo.data);
18004         this.setStore(this.store, true);
18005     }
18006     
18007     if ( this.footer && this.footer.xtype) {
18008            
18009          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18010         
18011         this.footer.dataSource = this.store;
18012         this.footer.container = fctr;
18013         this.footer = Roo.factory(this.footer, Roo);
18014         fctr.insertFirst(this.el);
18015         
18016         // this is a bit insane - as the paging toolbar seems to detach the el..
18017 //        dom.parentNode.parentNode.parentNode
18018          // they get detached?
18019     }
18020     
18021     
18022     Roo.View.superclass.constructor.call(this);
18023     
18024     
18025 };
18026
18027 Roo.extend(Roo.View, Roo.util.Observable, {
18028     
18029      /**
18030      * @cfg {Roo.data.Store} store Data store to load data from.
18031      */
18032     store : false,
18033     
18034     /**
18035      * @cfg {String|Roo.Element} el The container element.
18036      */
18037     el : '',
18038     
18039     /**
18040      * @cfg {String|Roo.Template} tpl The template used by this View 
18041      */
18042     tpl : false,
18043     /**
18044      * @cfg {String} dataName the named area of the template to use as the data area
18045      *                          Works with domtemplates roo-name="name"
18046      */
18047     dataName: false,
18048     /**
18049      * @cfg {String} selectedClass The css class to add to selected nodes
18050      */
18051     selectedClass : "x-view-selected",
18052      /**
18053      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18054      */
18055     emptyText : "",
18056     
18057     /**
18058      * @cfg {String} text to display on mask (default Loading)
18059      */
18060     mask : false,
18061     /**
18062      * @cfg {Boolean} multiSelect Allow multiple selection
18063      */
18064     multiSelect : false,
18065     /**
18066      * @cfg {Boolean} singleSelect Allow single selection
18067      */
18068     singleSelect:  false,
18069     
18070     /**
18071      * @cfg {Boolean} toggleSelect - selecting 
18072      */
18073     toggleSelect : false,
18074     
18075     /**
18076      * @cfg {Boolean} tickable - selecting 
18077      */
18078     tickable : false,
18079     
18080     /**
18081      * Returns the element this view is bound to.
18082      * @return {Roo.Element}
18083      */
18084     getEl : function(){
18085         return this.wrapEl;
18086     },
18087     
18088     
18089
18090     /**
18091      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18092      */
18093     refresh : function(){
18094         //Roo.log('refresh');
18095         var t = this.tpl;
18096         
18097         // if we are using something like 'domtemplate', then
18098         // the what gets used is:
18099         // t.applySubtemplate(NAME, data, wrapping data..)
18100         // the outer template then get' applied with
18101         //     the store 'extra data'
18102         // and the body get's added to the
18103         //      roo-name="data" node?
18104         //      <span class='roo-tpl-{name}'></span> ?????
18105         
18106         
18107         
18108         this.clearSelections();
18109         this.el.update("");
18110         var html = [];
18111         var records = this.store.getRange();
18112         if(records.length < 1) {
18113             
18114             // is this valid??  = should it render a template??
18115             
18116             this.el.update(this.emptyText);
18117             return;
18118         }
18119         var el = this.el;
18120         if (this.dataName) {
18121             this.el.update(t.apply(this.store.meta)); //????
18122             el = this.el.child('.roo-tpl-' + this.dataName);
18123         }
18124         
18125         for(var i = 0, len = records.length; i < len; i++){
18126             var data = this.prepareData(records[i].data, i, records[i]);
18127             this.fireEvent("preparedata", this, data, i, records[i]);
18128             
18129             var d = Roo.apply({}, data);
18130             
18131             if(this.tickable){
18132                 Roo.apply(d, {'roo-id' : Roo.id()});
18133                 
18134                 var _this = this;
18135             
18136                 Roo.each(this.parent.item, function(item){
18137                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18138                         return;
18139                     }
18140                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18141                 });
18142             }
18143             
18144             html[html.length] = Roo.util.Format.trim(
18145                 this.dataName ?
18146                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18147                     t.apply(d)
18148             );
18149         }
18150         
18151         
18152         
18153         el.update(html.join(""));
18154         this.nodes = el.dom.childNodes;
18155         this.updateIndexes(0);
18156     },
18157     
18158
18159     /**
18160      * Function to override to reformat the data that is sent to
18161      * the template for each node.
18162      * DEPRICATED - use the preparedata event handler.
18163      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18164      * a JSON object for an UpdateManager bound view).
18165      */
18166     prepareData : function(data, index, record)
18167     {
18168         this.fireEvent("preparedata", this, data, index, record);
18169         return data;
18170     },
18171
18172     onUpdate : function(ds, record){
18173         // Roo.log('on update');   
18174         this.clearSelections();
18175         var index = this.store.indexOf(record);
18176         var n = this.nodes[index];
18177         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18178         n.parentNode.removeChild(n);
18179         this.updateIndexes(index, index);
18180     },
18181
18182     
18183     
18184 // --------- FIXME     
18185     onAdd : function(ds, records, index)
18186     {
18187         //Roo.log(['on Add', ds, records, index] );        
18188         this.clearSelections();
18189         if(this.nodes.length == 0){
18190             this.refresh();
18191             return;
18192         }
18193         var n = this.nodes[index];
18194         for(var i = 0, len = records.length; i < len; i++){
18195             var d = this.prepareData(records[i].data, i, records[i]);
18196             if(n){
18197                 this.tpl.insertBefore(n, d);
18198             }else{
18199                 
18200                 this.tpl.append(this.el, d);
18201             }
18202         }
18203         this.updateIndexes(index);
18204     },
18205
18206     onRemove : function(ds, record, index){
18207        // Roo.log('onRemove');
18208         this.clearSelections();
18209         var el = this.dataName  ?
18210             this.el.child('.roo-tpl-' + this.dataName) :
18211             this.el; 
18212         
18213         el.dom.removeChild(this.nodes[index]);
18214         this.updateIndexes(index);
18215     },
18216
18217     /**
18218      * Refresh an individual node.
18219      * @param {Number} index
18220      */
18221     refreshNode : function(index){
18222         this.onUpdate(this.store, this.store.getAt(index));
18223     },
18224
18225     updateIndexes : function(startIndex, endIndex){
18226         var ns = this.nodes;
18227         startIndex = startIndex || 0;
18228         endIndex = endIndex || ns.length - 1;
18229         for(var i = startIndex; i <= endIndex; i++){
18230             ns[i].nodeIndex = i;
18231         }
18232     },
18233
18234     /**
18235      * Changes the data store this view uses and refresh the view.
18236      * @param {Store} store
18237      */
18238     setStore : function(store, initial){
18239         if(!initial && this.store){
18240             this.store.un("datachanged", this.refresh);
18241             this.store.un("add", this.onAdd);
18242             this.store.un("remove", this.onRemove);
18243             this.store.un("update", this.onUpdate);
18244             this.store.un("clear", this.refresh);
18245             this.store.un("beforeload", this.onBeforeLoad);
18246             this.store.un("load", this.onLoad);
18247             this.store.un("loadexception", this.onLoad);
18248         }
18249         if(store){
18250           
18251             store.on("datachanged", this.refresh, this);
18252             store.on("add", this.onAdd, this);
18253             store.on("remove", this.onRemove, this);
18254             store.on("update", this.onUpdate, this);
18255             store.on("clear", this.refresh, this);
18256             store.on("beforeload", this.onBeforeLoad, this);
18257             store.on("load", this.onLoad, this);
18258             store.on("loadexception", this.onLoad, this);
18259         }
18260         
18261         if(store){
18262             this.refresh();
18263         }
18264     },
18265     /**
18266      * onbeforeLoad - masks the loading area.
18267      *
18268      */
18269     onBeforeLoad : function(store,opts)
18270     {
18271          //Roo.log('onBeforeLoad');   
18272         if (!opts.add) {
18273             this.el.update("");
18274         }
18275         this.el.mask(this.mask ? this.mask : "Loading" ); 
18276     },
18277     onLoad : function ()
18278     {
18279         this.el.unmask();
18280     },
18281     
18282
18283     /**
18284      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18285      * @param {HTMLElement} node
18286      * @return {HTMLElement} The template node
18287      */
18288     findItemFromChild : function(node){
18289         var el = this.dataName  ?
18290             this.el.child('.roo-tpl-' + this.dataName,true) :
18291             this.el.dom; 
18292         
18293         if(!node || node.parentNode == el){
18294                     return node;
18295             }
18296             var p = node.parentNode;
18297             while(p && p != el){
18298             if(p.parentNode == el){
18299                 return p;
18300             }
18301             p = p.parentNode;
18302         }
18303             return null;
18304     },
18305
18306     /** @ignore */
18307     onClick : function(e){
18308         var item = this.findItemFromChild(e.getTarget());
18309         if(item){
18310             var index = this.indexOf(item);
18311             if(this.onItemClick(item, index, e) !== false){
18312                 this.fireEvent("click", this, index, item, e);
18313             }
18314         }else{
18315             this.clearSelections();
18316         }
18317     },
18318
18319     /** @ignore */
18320     onContextMenu : function(e){
18321         var item = this.findItemFromChild(e.getTarget());
18322         if(item){
18323             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18324         }
18325     },
18326
18327     /** @ignore */
18328     onDblClick : function(e){
18329         var item = this.findItemFromChild(e.getTarget());
18330         if(item){
18331             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18332         }
18333     },
18334
18335     onItemClick : function(item, index, e)
18336     {
18337         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18338             return false;
18339         }
18340         if (this.toggleSelect) {
18341             var m = this.isSelected(item) ? 'unselect' : 'select';
18342             //Roo.log(m);
18343             var _t = this;
18344             _t[m](item, true, false);
18345             return true;
18346         }
18347         if(this.multiSelect || this.singleSelect){
18348             if(this.multiSelect && e.shiftKey && this.lastSelection){
18349                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18350             }else{
18351                 this.select(item, this.multiSelect && e.ctrlKey);
18352                 this.lastSelection = item;
18353             }
18354             
18355             if(!this.tickable){
18356                 e.preventDefault();
18357             }
18358             
18359         }
18360         return true;
18361     },
18362
18363     /**
18364      * Get the number of selected nodes.
18365      * @return {Number}
18366      */
18367     getSelectionCount : function(){
18368         return this.selections.length;
18369     },
18370
18371     /**
18372      * Get the currently selected nodes.
18373      * @return {Array} An array of HTMLElements
18374      */
18375     getSelectedNodes : function(){
18376         return this.selections;
18377     },
18378
18379     /**
18380      * Get the indexes of the selected nodes.
18381      * @return {Array}
18382      */
18383     getSelectedIndexes : function(){
18384         var indexes = [], s = this.selections;
18385         for(var i = 0, len = s.length; i < len; i++){
18386             indexes.push(s[i].nodeIndex);
18387         }
18388         return indexes;
18389     },
18390
18391     /**
18392      * Clear all selections
18393      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18394      */
18395     clearSelections : function(suppressEvent){
18396         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18397             this.cmp.elements = this.selections;
18398             this.cmp.removeClass(this.selectedClass);
18399             this.selections = [];
18400             if(!suppressEvent){
18401                 this.fireEvent("selectionchange", this, this.selections);
18402             }
18403         }
18404     },
18405
18406     /**
18407      * Returns true if the passed node is selected
18408      * @param {HTMLElement/Number} node The node or node index
18409      * @return {Boolean}
18410      */
18411     isSelected : function(node){
18412         var s = this.selections;
18413         if(s.length < 1){
18414             return false;
18415         }
18416         node = this.getNode(node);
18417         return s.indexOf(node) !== -1;
18418     },
18419
18420     /**
18421      * Selects nodes.
18422      * @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
18423      * @param {Boolean} keepExisting (optional) true to keep existing selections
18424      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18425      */
18426     select : function(nodeInfo, keepExisting, suppressEvent){
18427         if(nodeInfo instanceof Array){
18428             if(!keepExisting){
18429                 this.clearSelections(true);
18430             }
18431             for(var i = 0, len = nodeInfo.length; i < len; i++){
18432                 this.select(nodeInfo[i], true, true);
18433             }
18434             return;
18435         } 
18436         var node = this.getNode(nodeInfo);
18437         if(!node || this.isSelected(node)){
18438             return; // already selected.
18439         }
18440         if(!keepExisting){
18441             this.clearSelections(true);
18442         }
18443         
18444         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18445             Roo.fly(node).addClass(this.selectedClass);
18446             this.selections.push(node);
18447             if(!suppressEvent){
18448                 this.fireEvent("selectionchange", this, this.selections);
18449             }
18450         }
18451         
18452         
18453     },
18454       /**
18455      * Unselects nodes.
18456      * @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
18457      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18458      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18459      */
18460     unselect : function(nodeInfo, keepExisting, suppressEvent)
18461     {
18462         if(nodeInfo instanceof Array){
18463             Roo.each(this.selections, function(s) {
18464                 this.unselect(s, nodeInfo);
18465             }, this);
18466             return;
18467         }
18468         var node = this.getNode(nodeInfo);
18469         if(!node || !this.isSelected(node)){
18470             //Roo.log("not selected");
18471             return; // not selected.
18472         }
18473         // fireevent???
18474         var ns = [];
18475         Roo.each(this.selections, function(s) {
18476             if (s == node ) {
18477                 Roo.fly(node).removeClass(this.selectedClass);
18478
18479                 return;
18480             }
18481             ns.push(s);
18482         },this);
18483         
18484         this.selections= ns;
18485         this.fireEvent("selectionchange", this, this.selections);
18486     },
18487
18488     /**
18489      * Gets a template node.
18490      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18491      * @return {HTMLElement} The node or null if it wasn't found
18492      */
18493     getNode : function(nodeInfo){
18494         if(typeof nodeInfo == "string"){
18495             return document.getElementById(nodeInfo);
18496         }else if(typeof nodeInfo == "number"){
18497             return this.nodes[nodeInfo];
18498         }
18499         return nodeInfo;
18500     },
18501
18502     /**
18503      * Gets a range template nodes.
18504      * @param {Number} startIndex
18505      * @param {Number} endIndex
18506      * @return {Array} An array of nodes
18507      */
18508     getNodes : function(start, end){
18509         var ns = this.nodes;
18510         start = start || 0;
18511         end = typeof end == "undefined" ? ns.length - 1 : end;
18512         var nodes = [];
18513         if(start <= end){
18514             for(var i = start; i <= end; i++){
18515                 nodes.push(ns[i]);
18516             }
18517         } else{
18518             for(var i = start; i >= end; i--){
18519                 nodes.push(ns[i]);
18520             }
18521         }
18522         return nodes;
18523     },
18524
18525     /**
18526      * Finds the index of the passed node
18527      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18528      * @return {Number} The index of the node or -1
18529      */
18530     indexOf : function(node){
18531         node = this.getNode(node);
18532         if(typeof node.nodeIndex == "number"){
18533             return node.nodeIndex;
18534         }
18535         var ns = this.nodes;
18536         for(var i = 0, len = ns.length; i < len; i++){
18537             if(ns[i] == node){
18538                 return i;
18539             }
18540         }
18541         return -1;
18542     }
18543 });
18544 /*
18545  * - LGPL
18546  *
18547  * based on jquery fullcalendar
18548  * 
18549  */
18550
18551 Roo.bootstrap = Roo.bootstrap || {};
18552 /**
18553  * @class Roo.bootstrap.Calendar
18554  * @extends Roo.bootstrap.Component
18555  * Bootstrap Calendar class
18556  * @cfg {Boolean} loadMask (true|false) default false
18557  * @cfg {Object} header generate the user specific header of the calendar, default false
18558
18559  * @constructor
18560  * Create a new Container
18561  * @param {Object} config The config object
18562  */
18563
18564
18565
18566 Roo.bootstrap.Calendar = function(config){
18567     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18568      this.addEvents({
18569         /**
18570              * @event select
18571              * Fires when a date is selected
18572              * @param {DatePicker} this
18573              * @param {Date} date The selected date
18574              */
18575         'select': true,
18576         /**
18577              * @event monthchange
18578              * Fires when the displayed month changes 
18579              * @param {DatePicker} this
18580              * @param {Date} date The selected month
18581              */
18582         'monthchange': true,
18583         /**
18584              * @event evententer
18585              * Fires when mouse over an event
18586              * @param {Calendar} this
18587              * @param {event} Event
18588              */
18589         'evententer': true,
18590         /**
18591              * @event eventleave
18592              * Fires when the mouse leaves an
18593              * @param {Calendar} this
18594              * @param {event}
18595              */
18596         'eventleave': true,
18597         /**
18598              * @event eventclick
18599              * Fires when the mouse click an
18600              * @param {Calendar} this
18601              * @param {event}
18602              */
18603         'eventclick': true
18604         
18605     });
18606
18607 };
18608
18609 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18610     
18611      /**
18612      * @cfg {Number} startDay
18613      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18614      */
18615     startDay : 0,
18616     
18617     loadMask : false,
18618     
18619     header : false,
18620       
18621     getAutoCreate : function(){
18622         
18623         
18624         var fc_button = function(name, corner, style, content ) {
18625             return Roo.apply({},{
18626                 tag : 'span',
18627                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18628                          (corner.length ?
18629                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18630                             ''
18631                         ),
18632                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18633                 unselectable: 'on'
18634             });
18635         };
18636         
18637         var header = {};
18638         
18639         if(!this.header){
18640             header = {
18641                 tag : 'table',
18642                 cls : 'fc-header',
18643                 style : 'width:100%',
18644                 cn : [
18645                     {
18646                         tag: 'tr',
18647                         cn : [
18648                             {
18649                                 tag : 'td',
18650                                 cls : 'fc-header-left',
18651                                 cn : [
18652                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18653                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18654                                     { tag: 'span', cls: 'fc-header-space' },
18655                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18656
18657
18658                                 ]
18659                             },
18660
18661                             {
18662                                 tag : 'td',
18663                                 cls : 'fc-header-center',
18664                                 cn : [
18665                                     {
18666                                         tag: 'span',
18667                                         cls: 'fc-header-title',
18668                                         cn : {
18669                                             tag: 'H2',
18670                                             html : 'month / year'
18671                                         }
18672                                     }
18673
18674                                 ]
18675                             },
18676                             {
18677                                 tag : 'td',
18678                                 cls : 'fc-header-right',
18679                                 cn : [
18680                               /*      fc_button('month', 'left', '', 'month' ),
18681                                     fc_button('week', '', '', 'week' ),
18682                                     fc_button('day', 'right', '', 'day' )
18683                                 */    
18684
18685                                 ]
18686                             }
18687
18688                         ]
18689                     }
18690                 ]
18691             };
18692         }
18693         
18694         header = this.header;
18695         
18696        
18697         var cal_heads = function() {
18698             var ret = [];
18699             // fixme - handle this.
18700             
18701             for (var i =0; i < Date.dayNames.length; i++) {
18702                 var d = Date.dayNames[i];
18703                 ret.push({
18704                     tag: 'th',
18705                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18706                     html : d.substring(0,3)
18707                 });
18708                 
18709             }
18710             ret[0].cls += ' fc-first';
18711             ret[6].cls += ' fc-last';
18712             return ret;
18713         };
18714         var cal_cell = function(n) {
18715             return  {
18716                 tag: 'td',
18717                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18718                 cn : [
18719                     {
18720                         cn : [
18721                             {
18722                                 cls: 'fc-day-number',
18723                                 html: 'D'
18724                             },
18725                             {
18726                                 cls: 'fc-day-content',
18727                              
18728                                 cn : [
18729                                      {
18730                                         style: 'position: relative;' // height: 17px;
18731                                     }
18732                                 ]
18733                             }
18734                             
18735                             
18736                         ]
18737                     }
18738                 ]
18739                 
18740             }
18741         };
18742         var cal_rows = function() {
18743             
18744             var ret = [];
18745             for (var r = 0; r < 6; r++) {
18746                 var row= {
18747                     tag : 'tr',
18748                     cls : 'fc-week',
18749                     cn : []
18750                 };
18751                 
18752                 for (var i =0; i < Date.dayNames.length; i++) {
18753                     var d = Date.dayNames[i];
18754                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18755
18756                 }
18757                 row.cn[0].cls+=' fc-first';
18758                 row.cn[0].cn[0].style = 'min-height:90px';
18759                 row.cn[6].cls+=' fc-last';
18760                 ret.push(row);
18761                 
18762             }
18763             ret[0].cls += ' fc-first';
18764             ret[4].cls += ' fc-prev-last';
18765             ret[5].cls += ' fc-last';
18766             return ret;
18767             
18768         };
18769         
18770         var cal_table = {
18771             tag: 'table',
18772             cls: 'fc-border-separate',
18773             style : 'width:100%',
18774             cellspacing  : 0,
18775             cn : [
18776                 { 
18777                     tag: 'thead',
18778                     cn : [
18779                         { 
18780                             tag: 'tr',
18781                             cls : 'fc-first fc-last',
18782                             cn : cal_heads()
18783                         }
18784                     ]
18785                 },
18786                 { 
18787                     tag: 'tbody',
18788                     cn : cal_rows()
18789                 }
18790                   
18791             ]
18792         };
18793          
18794          var cfg = {
18795             cls : 'fc fc-ltr',
18796             cn : [
18797                 header,
18798                 {
18799                     cls : 'fc-content',
18800                     style : "position: relative;",
18801                     cn : [
18802                         {
18803                             cls : 'fc-view fc-view-month fc-grid',
18804                             style : 'position: relative',
18805                             unselectable : 'on',
18806                             cn : [
18807                                 {
18808                                     cls : 'fc-event-container',
18809                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18810                                 },
18811                                 cal_table
18812                             ]
18813                         }
18814                     ]
18815     
18816                 }
18817            ] 
18818             
18819         };
18820         
18821          
18822         
18823         return cfg;
18824     },
18825     
18826     
18827     initEvents : function()
18828     {
18829         if(!this.store){
18830             throw "can not find store for calendar";
18831         }
18832         
18833         var mark = {
18834             tag: "div",
18835             cls:"x-dlg-mask",
18836             style: "text-align:center",
18837             cn: [
18838                 {
18839                     tag: "div",
18840                     style: "background-color:white;width:50%;margin:250 auto",
18841                     cn: [
18842                         {
18843                             tag: "img",
18844                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18845                         },
18846                         {
18847                             tag: "span",
18848                             html: "Loading"
18849                         }
18850                         
18851                     ]
18852                 }
18853             ]
18854         };
18855         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18856         
18857         var size = this.el.select('.fc-content', true).first().getSize();
18858         this.maskEl.setSize(size.width, size.height);
18859         this.maskEl.enableDisplayMode("block");
18860         if(!this.loadMask){
18861             this.maskEl.hide();
18862         }
18863         
18864         this.store = Roo.factory(this.store, Roo.data);
18865         this.store.on('load', this.onLoad, this);
18866         this.store.on('beforeload', this.onBeforeLoad, this);
18867         
18868         this.resize();
18869         
18870         this.cells = this.el.select('.fc-day',true);
18871         //Roo.log(this.cells);
18872         this.textNodes = this.el.query('.fc-day-number');
18873         this.cells.addClassOnOver('fc-state-hover');
18874         
18875         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18876         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18877         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18878         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18879         
18880         this.on('monthchange', this.onMonthChange, this);
18881         
18882         this.update(new Date().clearTime());
18883     },
18884     
18885     resize : function() {
18886         var sz  = this.el.getSize();
18887         
18888         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18889         this.el.select('.fc-day-content div',true).setHeight(34);
18890     },
18891     
18892     
18893     // private
18894     showPrevMonth : function(e){
18895         this.update(this.activeDate.add("mo", -1));
18896     },
18897     showToday : function(e){
18898         this.update(new Date().clearTime());
18899     },
18900     // private
18901     showNextMonth : function(e){
18902         this.update(this.activeDate.add("mo", 1));
18903     },
18904
18905     // private
18906     showPrevYear : function(){
18907         this.update(this.activeDate.add("y", -1));
18908     },
18909
18910     // private
18911     showNextYear : function(){
18912         this.update(this.activeDate.add("y", 1));
18913     },
18914
18915     
18916    // private
18917     update : function(date)
18918     {
18919         var vd = this.activeDate;
18920         this.activeDate = date;
18921 //        if(vd && this.el){
18922 //            var t = date.getTime();
18923 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18924 //                Roo.log('using add remove');
18925 //                
18926 //                this.fireEvent('monthchange', this, date);
18927 //                
18928 //                this.cells.removeClass("fc-state-highlight");
18929 //                this.cells.each(function(c){
18930 //                   if(c.dateValue == t){
18931 //                       c.addClass("fc-state-highlight");
18932 //                       setTimeout(function(){
18933 //                            try{c.dom.firstChild.focus();}catch(e){}
18934 //                       }, 50);
18935 //                       return false;
18936 //                   }
18937 //                   return true;
18938 //                });
18939 //                return;
18940 //            }
18941 //        }
18942         
18943         var days = date.getDaysInMonth();
18944         
18945         var firstOfMonth = date.getFirstDateOfMonth();
18946         var startingPos = firstOfMonth.getDay()-this.startDay;
18947         
18948         if(startingPos < this.startDay){
18949             startingPos += 7;
18950         }
18951         
18952         var pm = date.add(Date.MONTH, -1);
18953         var prevStart = pm.getDaysInMonth()-startingPos;
18954 //        
18955         this.cells = this.el.select('.fc-day',true);
18956         this.textNodes = this.el.query('.fc-day-number');
18957         this.cells.addClassOnOver('fc-state-hover');
18958         
18959         var cells = this.cells.elements;
18960         var textEls = this.textNodes;
18961         
18962         Roo.each(cells, function(cell){
18963             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18964         });
18965         
18966         days += startingPos;
18967
18968         // convert everything to numbers so it's fast
18969         var day = 86400000;
18970         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18971         //Roo.log(d);
18972         //Roo.log(pm);
18973         //Roo.log(prevStart);
18974         
18975         var today = new Date().clearTime().getTime();
18976         var sel = date.clearTime().getTime();
18977         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18978         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18979         var ddMatch = this.disabledDatesRE;
18980         var ddText = this.disabledDatesText;
18981         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18982         var ddaysText = this.disabledDaysText;
18983         var format = this.format;
18984         
18985         var setCellClass = function(cal, cell){
18986             cell.row = 0;
18987             cell.events = [];
18988             cell.more = [];
18989             //Roo.log('set Cell Class');
18990             cell.title = "";
18991             var t = d.getTime();
18992             
18993             //Roo.log(d);
18994             
18995             cell.dateValue = t;
18996             if(t == today){
18997                 cell.className += " fc-today";
18998                 cell.className += " fc-state-highlight";
18999                 cell.title = cal.todayText;
19000             }
19001             if(t == sel){
19002                 // disable highlight in other month..
19003                 //cell.className += " fc-state-highlight";
19004                 
19005             }
19006             // disabling
19007             if(t < min) {
19008                 cell.className = " fc-state-disabled";
19009                 cell.title = cal.minText;
19010                 return;
19011             }
19012             if(t > max) {
19013                 cell.className = " fc-state-disabled";
19014                 cell.title = cal.maxText;
19015                 return;
19016             }
19017             if(ddays){
19018                 if(ddays.indexOf(d.getDay()) != -1){
19019                     cell.title = ddaysText;
19020                     cell.className = " fc-state-disabled";
19021                 }
19022             }
19023             if(ddMatch && format){
19024                 var fvalue = d.dateFormat(format);
19025                 if(ddMatch.test(fvalue)){
19026                     cell.title = ddText.replace("%0", fvalue);
19027                     cell.className = " fc-state-disabled";
19028                 }
19029             }
19030             
19031             if (!cell.initialClassName) {
19032                 cell.initialClassName = cell.dom.className;
19033             }
19034             
19035             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19036         };
19037
19038         var i = 0;
19039         
19040         for(; i < startingPos; i++) {
19041             textEls[i].innerHTML = (++prevStart);
19042             d.setDate(d.getDate()+1);
19043             
19044             cells[i].className = "fc-past fc-other-month";
19045             setCellClass(this, cells[i]);
19046         }
19047         
19048         var intDay = 0;
19049         
19050         for(; i < days; i++){
19051             intDay = i - startingPos + 1;
19052             textEls[i].innerHTML = (intDay);
19053             d.setDate(d.getDate()+1);
19054             
19055             cells[i].className = ''; // "x-date-active";
19056             setCellClass(this, cells[i]);
19057         }
19058         var extraDays = 0;
19059         
19060         for(; i < 42; i++) {
19061             textEls[i].innerHTML = (++extraDays);
19062             d.setDate(d.getDate()+1);
19063             
19064             cells[i].className = "fc-future fc-other-month";
19065             setCellClass(this, cells[i]);
19066         }
19067         
19068         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19069         
19070         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19071         
19072         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19073         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19074         
19075         if(totalRows != 6){
19076             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19077             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19078         }
19079         
19080         this.fireEvent('monthchange', this, date);
19081         
19082         
19083         /*
19084         if(!this.internalRender){
19085             var main = this.el.dom.firstChild;
19086             var w = main.offsetWidth;
19087             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19088             Roo.fly(main).setWidth(w);
19089             this.internalRender = true;
19090             // opera does not respect the auto grow header center column
19091             // then, after it gets a width opera refuses to recalculate
19092             // without a second pass
19093             if(Roo.isOpera && !this.secondPass){
19094                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19095                 this.secondPass = true;
19096                 this.update.defer(10, this, [date]);
19097             }
19098         }
19099         */
19100         
19101     },
19102     
19103     findCell : function(dt) {
19104         dt = dt.clearTime().getTime();
19105         var ret = false;
19106         this.cells.each(function(c){
19107             //Roo.log("check " +c.dateValue + '?=' + dt);
19108             if(c.dateValue == dt){
19109                 ret = c;
19110                 return false;
19111             }
19112             return true;
19113         });
19114         
19115         return ret;
19116     },
19117     
19118     findCells : function(ev) {
19119         var s = ev.start.clone().clearTime().getTime();
19120        // Roo.log(s);
19121         var e= ev.end.clone().clearTime().getTime();
19122        // Roo.log(e);
19123         var ret = [];
19124         this.cells.each(function(c){
19125              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19126             
19127             if(c.dateValue > e){
19128                 return ;
19129             }
19130             if(c.dateValue < s){
19131                 return ;
19132             }
19133             ret.push(c);
19134         });
19135         
19136         return ret;    
19137     },
19138     
19139 //    findBestRow: function(cells)
19140 //    {
19141 //        var ret = 0;
19142 //        
19143 //        for (var i =0 ; i < cells.length;i++) {
19144 //            ret  = Math.max(cells[i].rows || 0,ret);
19145 //        }
19146 //        return ret;
19147 //        
19148 //    },
19149     
19150     
19151     addItem : function(ev)
19152     {
19153         // look for vertical location slot in
19154         var cells = this.findCells(ev);
19155         
19156 //        ev.row = this.findBestRow(cells);
19157         
19158         // work out the location.
19159         
19160         var crow = false;
19161         var rows = [];
19162         for(var i =0; i < cells.length; i++) {
19163             
19164             cells[i].row = cells[0].row;
19165             
19166             if(i == 0){
19167                 cells[i].row = cells[i].row + 1;
19168             }
19169             
19170             if (!crow) {
19171                 crow = {
19172                     start : cells[i],
19173                     end :  cells[i]
19174                 };
19175                 continue;
19176             }
19177             if (crow.start.getY() == cells[i].getY()) {
19178                 // on same row.
19179                 crow.end = cells[i];
19180                 continue;
19181             }
19182             // different row.
19183             rows.push(crow);
19184             crow = {
19185                 start: cells[i],
19186                 end : cells[i]
19187             };
19188             
19189         }
19190         
19191         rows.push(crow);
19192         ev.els = [];
19193         ev.rows = rows;
19194         ev.cells = cells;
19195         
19196         cells[0].events.push(ev);
19197         
19198         this.calevents.push(ev);
19199     },
19200     
19201     clearEvents: function() {
19202         
19203         if(!this.calevents){
19204             return;
19205         }
19206         
19207         Roo.each(this.cells.elements, function(c){
19208             c.row = 0;
19209             c.events = [];
19210             c.more = [];
19211         });
19212         
19213         Roo.each(this.calevents, function(e) {
19214             Roo.each(e.els, function(el) {
19215                 el.un('mouseenter' ,this.onEventEnter, this);
19216                 el.un('mouseleave' ,this.onEventLeave, this);
19217                 el.remove();
19218             },this);
19219         },this);
19220         
19221         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19222             e.remove();
19223         });
19224         
19225     },
19226     
19227     renderEvents: function()
19228     {   
19229         var _this = this;
19230         
19231         this.cells.each(function(c) {
19232             
19233             if(c.row < 5){
19234                 return;
19235             }
19236             
19237             var ev = c.events;
19238             
19239             var r = 4;
19240             if(c.row != c.events.length){
19241                 r = 4 - (4 - (c.row - c.events.length));
19242             }
19243             
19244             c.events = ev.slice(0, r);
19245             c.more = ev.slice(r);
19246             
19247             if(c.more.length && c.more.length == 1){
19248                 c.events.push(c.more.pop());
19249             }
19250             
19251             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19252             
19253         });
19254             
19255         this.cells.each(function(c) {
19256             
19257             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19258             
19259             
19260             for (var e = 0; e < c.events.length; e++){
19261                 var ev = c.events[e];
19262                 var rows = ev.rows;
19263                 
19264                 for(var i = 0; i < rows.length; i++) {
19265                 
19266                     // how many rows should it span..
19267
19268                     var  cfg = {
19269                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19270                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19271
19272                         unselectable : "on",
19273                         cn : [
19274                             {
19275                                 cls: 'fc-event-inner',
19276                                 cn : [
19277     //                                {
19278     //                                  tag:'span',
19279     //                                  cls: 'fc-event-time',
19280     //                                  html : cells.length > 1 ? '' : ev.time
19281     //                                },
19282                                     {
19283                                       tag:'span',
19284                                       cls: 'fc-event-title',
19285                                       html : String.format('{0}', ev.title)
19286                                     }
19287
19288
19289                                 ]
19290                             },
19291                             {
19292                                 cls: 'ui-resizable-handle ui-resizable-e',
19293                                 html : '&nbsp;&nbsp;&nbsp'
19294                             }
19295
19296                         ]
19297                     };
19298
19299                     if (i == 0) {
19300                         cfg.cls += ' fc-event-start';
19301                     }
19302                     if ((i+1) == rows.length) {
19303                         cfg.cls += ' fc-event-end';
19304                     }
19305
19306                     var ctr = _this.el.select('.fc-event-container',true).first();
19307                     var cg = ctr.createChild(cfg);
19308
19309                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19310                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19311
19312                     var r = (c.more.length) ? 1 : 0;
19313                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19314                     cg.setWidth(ebox.right - sbox.x -2);
19315
19316                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19317                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19318                     cg.on('click', _this.onEventClick, _this, ev);
19319
19320                     ev.els.push(cg);
19321                     
19322                 }
19323                 
19324             }
19325             
19326             
19327             if(c.more.length){
19328                 var  cfg = {
19329                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19330                     style : 'position: absolute',
19331                     unselectable : "on",
19332                     cn : [
19333                         {
19334                             cls: 'fc-event-inner',
19335                             cn : [
19336                                 {
19337                                   tag:'span',
19338                                   cls: 'fc-event-title',
19339                                   html : 'More'
19340                                 }
19341
19342
19343                             ]
19344                         },
19345                         {
19346                             cls: 'ui-resizable-handle ui-resizable-e',
19347                             html : '&nbsp;&nbsp;&nbsp'
19348                         }
19349
19350                     ]
19351                 };
19352
19353                 var ctr = _this.el.select('.fc-event-container',true).first();
19354                 var cg = ctr.createChild(cfg);
19355
19356                 var sbox = c.select('.fc-day-content',true).first().getBox();
19357                 var ebox = c.select('.fc-day-content',true).first().getBox();
19358                 //Roo.log(cg);
19359                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19360                 cg.setWidth(ebox.right - sbox.x -2);
19361
19362                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19363                 
19364             }
19365             
19366         });
19367         
19368         
19369         
19370     },
19371     
19372     onEventEnter: function (e, el,event,d) {
19373         this.fireEvent('evententer', this, el, event);
19374     },
19375     
19376     onEventLeave: function (e, el,event,d) {
19377         this.fireEvent('eventleave', this, el, event);
19378     },
19379     
19380     onEventClick: function (e, el,event,d) {
19381         this.fireEvent('eventclick', this, el, event);
19382     },
19383     
19384     onMonthChange: function () {
19385         this.store.load();
19386     },
19387     
19388     onMoreEventClick: function(e, el, more)
19389     {
19390         var _this = this;
19391         
19392         this.calpopover.placement = 'right';
19393         this.calpopover.setTitle('More');
19394         
19395         this.calpopover.setContent('');
19396         
19397         var ctr = this.calpopover.el.select('.popover-content', true).first();
19398         
19399         Roo.each(more, function(m){
19400             var cfg = {
19401                 cls : 'fc-event-hori fc-event-draggable',
19402                 html : m.title
19403             };
19404             var cg = ctr.createChild(cfg);
19405             
19406             cg.on('click', _this.onEventClick, _this, m);
19407         });
19408         
19409         this.calpopover.show(el);
19410         
19411         
19412     },
19413     
19414     onLoad: function () 
19415     {   
19416         this.calevents = [];
19417         var cal = this;
19418         
19419         if(this.store.getCount() > 0){
19420             this.store.data.each(function(d){
19421                cal.addItem({
19422                     id : d.data.id,
19423                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19424                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19425                     time : d.data.start_time,
19426                     title : d.data.title,
19427                     description : d.data.description,
19428                     venue : d.data.venue
19429                 });
19430             });
19431         }
19432         
19433         this.renderEvents();
19434         
19435         if(this.calevents.length && this.loadMask){
19436             this.maskEl.hide();
19437         }
19438     },
19439     
19440     onBeforeLoad: function()
19441     {
19442         this.clearEvents();
19443         if(this.loadMask){
19444             this.maskEl.show();
19445         }
19446     }
19447 });
19448
19449  
19450  /*
19451  * - LGPL
19452  *
19453  * element
19454  * 
19455  */
19456
19457 /**
19458  * @class Roo.bootstrap.Popover
19459  * @extends Roo.bootstrap.Component
19460  * Bootstrap Popover class
19461  * @cfg {String} html contents of the popover   (or false to use children..)
19462  * @cfg {String} title of popover (or false to hide)
19463  * @cfg {String} placement how it is placed
19464  * @cfg {String} trigger click || hover (or false to trigger manually)
19465  * @cfg {String} over what (parent or false to trigger manually.)
19466  * @cfg {Number} delay - delay before showing
19467  
19468  * @constructor
19469  * Create a new Popover
19470  * @param {Object} config The config object
19471  */
19472
19473 Roo.bootstrap.Popover = function(config){
19474     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19475     
19476     this.addEvents({
19477         // raw events
19478          /**
19479          * @event show
19480          * After the popover show
19481          * 
19482          * @param {Roo.bootstrap.Popover} this
19483          */
19484         "show" : true,
19485         /**
19486          * @event hide
19487          * After the popover hide
19488          * 
19489          * @param {Roo.bootstrap.Popover} this
19490          */
19491         "hide" : true
19492     });
19493 };
19494
19495 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19496     
19497     title: 'Fill in a title',
19498     html: false,
19499     
19500     placement : 'right',
19501     trigger : 'hover', // hover
19502     
19503     delay : 0,
19504     
19505     over: 'parent',
19506     
19507     can_build_overlaid : false,
19508     
19509     getChildContainer : function()
19510     {
19511         return this.el.select('.popover-content',true).first();
19512     },
19513     
19514     getAutoCreate : function(){
19515          
19516         var cfg = {
19517            cls : 'popover roo-dynamic',
19518            style: 'display:block',
19519            cn : [
19520                 {
19521                     cls : 'arrow'
19522                 },
19523                 {
19524                     cls : 'popover-inner',
19525                     cn : [
19526                         {
19527                             tag: 'h3',
19528                             cls: 'popover-title popover-header',
19529                             html : this.title
19530                         },
19531                         {
19532                             cls : 'popover-content popover-body',
19533                             html : this.html
19534                         }
19535                     ]
19536                     
19537                 }
19538            ]
19539         };
19540         
19541         return cfg;
19542     },
19543     setTitle: function(str)
19544     {
19545         this.title = str;
19546         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19547     },
19548     setContent: function(str)
19549     {
19550         this.html = str;
19551         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19552     },
19553     // as it get's added to the bottom of the page.
19554     onRender : function(ct, position)
19555     {
19556         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19557         if(!this.el){
19558             var cfg = Roo.apply({},  this.getAutoCreate());
19559             cfg.id = Roo.id();
19560             
19561             if (this.cls) {
19562                 cfg.cls += ' ' + this.cls;
19563             }
19564             if (this.style) {
19565                 cfg.style = this.style;
19566             }
19567             //Roo.log("adding to ");
19568             this.el = Roo.get(document.body).createChild(cfg, position);
19569 //            Roo.log(this.el);
19570         }
19571         this.initEvents();
19572     },
19573     
19574     initEvents : function()
19575     {
19576         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19577         this.el.enableDisplayMode('block');
19578         this.el.hide();
19579         if (this.over === false) {
19580             return; 
19581         }
19582         if (this.triggers === false) {
19583             return;
19584         }
19585         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19586         var triggers = this.trigger ? this.trigger.split(' ') : [];
19587         Roo.each(triggers, function(trigger) {
19588         
19589             if (trigger == 'click') {
19590                 on_el.on('click', this.toggle, this);
19591             } else if (trigger != 'manual') {
19592                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19593                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19594       
19595                 on_el.on(eventIn  ,this.enter, this);
19596                 on_el.on(eventOut, this.leave, this);
19597             }
19598         }, this);
19599         
19600     },
19601     
19602     
19603     // private
19604     timeout : null,
19605     hoverState : null,
19606     
19607     toggle : function () {
19608         this.hoverState == 'in' ? this.leave() : this.enter();
19609     },
19610     
19611     enter : function () {
19612         
19613         clearTimeout(this.timeout);
19614     
19615         this.hoverState = 'in';
19616     
19617         if (!this.delay || !this.delay.show) {
19618             this.show();
19619             return;
19620         }
19621         var _t = this;
19622         this.timeout = setTimeout(function () {
19623             if (_t.hoverState == 'in') {
19624                 _t.show();
19625             }
19626         }, this.delay.show)
19627     },
19628     
19629     leave : function() {
19630         clearTimeout(this.timeout);
19631     
19632         this.hoverState = 'out';
19633     
19634         if (!this.delay || !this.delay.hide) {
19635             this.hide();
19636             return;
19637         }
19638         var _t = this;
19639         this.timeout = setTimeout(function () {
19640             if (_t.hoverState == 'out') {
19641                 _t.hide();
19642             }
19643         }, this.delay.hide)
19644     },
19645     
19646     show : function (on_el)
19647     {
19648         if (!on_el) {
19649             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19650         }
19651         
19652         // set content.
19653         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19654         if (this.html !== false) {
19655             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19656         }
19657         this.el.removeClass([
19658             'fade','top','bottom', 'left', 'right','in',
19659             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19660         ]);
19661         if (!this.title.length) {
19662             this.el.select('.popover-title',true).hide();
19663         }
19664         
19665         var placement = typeof this.placement == 'function' ?
19666             this.placement.call(this, this.el, on_el) :
19667             this.placement;
19668             
19669         var autoToken = /\s?auto?\s?/i;
19670         var autoPlace = autoToken.test(placement);
19671         if (autoPlace) {
19672             placement = placement.replace(autoToken, '') || 'top';
19673         }
19674         
19675         //this.el.detach()
19676         //this.el.setXY([0,0]);
19677         this.el.show();
19678         this.el.dom.style.display='block';
19679         this.el.addClass(placement);
19680         
19681         //this.el.appendTo(on_el);
19682         
19683         var p = this.getPosition();
19684         var box = this.el.getBox();
19685         
19686         if (autoPlace) {
19687             // fixme..
19688         }
19689         var align = Roo.bootstrap.Popover.alignment[placement];
19690         
19691 //        Roo.log(align);
19692         this.el.alignTo(on_el, align[0],align[1]);
19693         //var arrow = this.el.select('.arrow',true).first();
19694         //arrow.set(align[2], 
19695         
19696         this.el.addClass('in');
19697         
19698         
19699         if (this.el.hasClass('fade')) {
19700             // fade it?
19701         }
19702         
19703         this.hoverState = 'in';
19704         
19705         this.fireEvent('show', this);
19706         
19707     },
19708     hide : function()
19709     {
19710         this.el.setXY([0,0]);
19711         this.el.removeClass('in');
19712         this.el.hide();
19713         this.hoverState = null;
19714         
19715         this.fireEvent('hide', this);
19716     }
19717     
19718 });
19719
19720 Roo.bootstrap.Popover.alignment = {
19721     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19722     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19723     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19724     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19725 };
19726
19727  /*
19728  * - LGPL
19729  *
19730  * Progress
19731  * 
19732  */
19733
19734 /**
19735  * @class Roo.bootstrap.Progress
19736  * @extends Roo.bootstrap.Component
19737  * Bootstrap Progress class
19738  * @cfg {Boolean} striped striped of the progress bar
19739  * @cfg {Boolean} active animated of the progress bar
19740  * 
19741  * 
19742  * @constructor
19743  * Create a new Progress
19744  * @param {Object} config The config object
19745  */
19746
19747 Roo.bootstrap.Progress = function(config){
19748     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19749 };
19750
19751 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19752     
19753     striped : false,
19754     active: false,
19755     
19756     getAutoCreate : function(){
19757         var cfg = {
19758             tag: 'div',
19759             cls: 'progress'
19760         };
19761         
19762         
19763         if(this.striped){
19764             cfg.cls += ' progress-striped';
19765         }
19766       
19767         if(this.active){
19768             cfg.cls += ' active';
19769         }
19770         
19771         
19772         return cfg;
19773     }
19774    
19775 });
19776
19777  
19778
19779  /*
19780  * - LGPL
19781  *
19782  * ProgressBar
19783  * 
19784  */
19785
19786 /**
19787  * @class Roo.bootstrap.ProgressBar
19788  * @extends Roo.bootstrap.Component
19789  * Bootstrap ProgressBar class
19790  * @cfg {Number} aria_valuenow aria-value now
19791  * @cfg {Number} aria_valuemin aria-value min
19792  * @cfg {Number} aria_valuemax aria-value max
19793  * @cfg {String} label label for the progress bar
19794  * @cfg {String} panel (success | info | warning | danger )
19795  * @cfg {String} role role of the progress bar
19796  * @cfg {String} sr_only text
19797  * 
19798  * 
19799  * @constructor
19800  * Create a new ProgressBar
19801  * @param {Object} config The config object
19802  */
19803
19804 Roo.bootstrap.ProgressBar = function(config){
19805     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19806 };
19807
19808 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19809     
19810     aria_valuenow : 0,
19811     aria_valuemin : 0,
19812     aria_valuemax : 100,
19813     label : false,
19814     panel : false,
19815     role : false,
19816     sr_only: false,
19817     
19818     getAutoCreate : function()
19819     {
19820         
19821         var cfg = {
19822             tag: 'div',
19823             cls: 'progress-bar',
19824             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19825         };
19826         
19827         if(this.sr_only){
19828             cfg.cn = {
19829                 tag: 'span',
19830                 cls: 'sr-only',
19831                 html: this.sr_only
19832             }
19833         }
19834         
19835         if(this.role){
19836             cfg.role = this.role;
19837         }
19838         
19839         if(this.aria_valuenow){
19840             cfg['aria-valuenow'] = this.aria_valuenow;
19841         }
19842         
19843         if(this.aria_valuemin){
19844             cfg['aria-valuemin'] = this.aria_valuemin;
19845         }
19846         
19847         if(this.aria_valuemax){
19848             cfg['aria-valuemax'] = this.aria_valuemax;
19849         }
19850         
19851         if(this.label && !this.sr_only){
19852             cfg.html = this.label;
19853         }
19854         
19855         if(this.panel){
19856             cfg.cls += ' progress-bar-' + this.panel;
19857         }
19858         
19859         return cfg;
19860     },
19861     
19862     update : function(aria_valuenow)
19863     {
19864         this.aria_valuenow = aria_valuenow;
19865         
19866         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19867     }
19868    
19869 });
19870
19871  
19872
19873  /*
19874  * - LGPL
19875  *
19876  * column
19877  * 
19878  */
19879
19880 /**
19881  * @class Roo.bootstrap.TabGroup
19882  * @extends Roo.bootstrap.Column
19883  * Bootstrap Column class
19884  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19885  * @cfg {Boolean} carousel true to make the group behave like a carousel
19886  * @cfg {Boolean} bullets show bullets for the panels
19887  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19888  * @cfg {Number} timer auto slide timer .. default 0 millisecond
19889  * @cfg {Boolean} showarrow (true|false) show arrow default true
19890  * 
19891  * @constructor
19892  * Create a new TabGroup
19893  * @param {Object} config The config object
19894  */
19895
19896 Roo.bootstrap.TabGroup = function(config){
19897     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19898     if (!this.navId) {
19899         this.navId = Roo.id();
19900     }
19901     this.tabs = [];
19902     Roo.bootstrap.TabGroup.register(this);
19903     
19904 };
19905
19906 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
19907     
19908     carousel : false,
19909     transition : false,
19910     bullets : 0,
19911     timer : 0,
19912     autoslide : false,
19913     slideFn : false,
19914     slideOnTouch : false,
19915     showarrow : true,
19916     
19917     getAutoCreate : function()
19918     {
19919         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19920         
19921         cfg.cls += ' tab-content';
19922         
19923         if (this.carousel) {
19924             cfg.cls += ' carousel slide';
19925             
19926             cfg.cn = [{
19927                cls : 'carousel-inner',
19928                cn : []
19929             }];
19930         
19931             if(this.bullets  && !Roo.isTouch){
19932                 
19933                 var bullets = {
19934                     cls : 'carousel-bullets',
19935                     cn : []
19936                 };
19937                
19938                 if(this.bullets_cls){
19939                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19940                 }
19941                 
19942                 bullets.cn.push({
19943                     cls : 'clear'
19944                 });
19945                 
19946                 cfg.cn[0].cn.push(bullets);
19947             }
19948             
19949             if(this.showarrow){
19950                 cfg.cn[0].cn.push({
19951                     tag : 'div',
19952                     class : 'carousel-arrow',
19953                     cn : [
19954                         {
19955                             tag : 'div',
19956                             class : 'carousel-prev',
19957                             cn : [
19958                                 {
19959                                     tag : 'i',
19960                                     class : 'fa fa-chevron-left'
19961                                 }
19962                             ]
19963                         },
19964                         {
19965                             tag : 'div',
19966                             class : 'carousel-next',
19967                             cn : [
19968                                 {
19969                                     tag : 'i',
19970                                     class : 'fa fa-chevron-right'
19971                                 }
19972                             ]
19973                         }
19974                     ]
19975                 });
19976             }
19977             
19978         }
19979         
19980         return cfg;
19981     },
19982     
19983     initEvents:  function()
19984     {
19985 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19986 //            this.el.on("touchstart", this.onTouchStart, this);
19987 //        }
19988         
19989         if(this.autoslide){
19990             var _this = this;
19991             
19992             this.slideFn = window.setInterval(function() {
19993                 _this.showPanelNext();
19994             }, this.timer);
19995         }
19996         
19997         if(this.showarrow){
19998             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19999             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20000         }
20001         
20002         
20003     },
20004     
20005 //    onTouchStart : function(e, el, o)
20006 //    {
20007 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20008 //            return;
20009 //        }
20010 //        
20011 //        this.showPanelNext();
20012 //    },
20013     
20014     
20015     getChildContainer : function()
20016     {
20017         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20018     },
20019     
20020     /**
20021     * register a Navigation item
20022     * @param {Roo.bootstrap.NavItem} the navitem to add
20023     */
20024     register : function(item)
20025     {
20026         this.tabs.push( item);
20027         item.navId = this.navId; // not really needed..
20028         this.addBullet();
20029     
20030     },
20031     
20032     getActivePanel : function()
20033     {
20034         var r = false;
20035         Roo.each(this.tabs, function(t) {
20036             if (t.active) {
20037                 r = t;
20038                 return false;
20039             }
20040             return null;
20041         });
20042         return r;
20043         
20044     },
20045     getPanelByName : function(n)
20046     {
20047         var r = false;
20048         Roo.each(this.tabs, function(t) {
20049             if (t.tabId == n) {
20050                 r = t;
20051                 return false;
20052             }
20053             return null;
20054         });
20055         return r;
20056     },
20057     indexOfPanel : function(p)
20058     {
20059         var r = false;
20060         Roo.each(this.tabs, function(t,i) {
20061             if (t.tabId == p.tabId) {
20062                 r = i;
20063                 return false;
20064             }
20065             return null;
20066         });
20067         return r;
20068     },
20069     /**
20070      * show a specific panel
20071      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20072      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20073      */
20074     showPanel : function (pan)
20075     {
20076         if(this.transition || typeof(pan) == 'undefined'){
20077             Roo.log("waiting for the transitionend");
20078             return false;
20079         }
20080         
20081         if (typeof(pan) == 'number') {
20082             pan = this.tabs[pan];
20083         }
20084         
20085         if (typeof(pan) == 'string') {
20086             pan = this.getPanelByName(pan);
20087         }
20088         
20089         var cur = this.getActivePanel();
20090         
20091         if(!pan || !cur){
20092             Roo.log('pan or acitve pan is undefined');
20093             return false;
20094         }
20095         
20096         if (pan.tabId == this.getActivePanel().tabId) {
20097             return true;
20098         }
20099         
20100         if (false === cur.fireEvent('beforedeactivate')) {
20101             return false;
20102         }
20103         
20104         if(this.bullets > 0 && !Roo.isTouch){
20105             this.setActiveBullet(this.indexOfPanel(pan));
20106         }
20107         
20108         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20109             
20110             //class="carousel-item carousel-item-next carousel-item-left"
20111             
20112             this.transition = true;
20113             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20114             var lr = dir == 'next' ? 'left' : 'right';
20115             pan.el.addClass(dir); // or prev
20116             pan.el.addClass('carousel-item-' + dir); // or prev
20117             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20118             cur.el.addClass(lr); // or right
20119             pan.el.addClass(lr);
20120             cur.el.addClass('carousel-item-' +lr); // or right
20121             pan.el.addClass('carousel-item-' +lr);
20122             
20123             
20124             var _this = this;
20125             cur.el.on('transitionend', function() {
20126                 Roo.log("trans end?");
20127                 
20128                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20129                 pan.setActive(true);
20130                 
20131                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20132                 cur.setActive(false);
20133                 
20134                 _this.transition = false;
20135                 
20136             }, this, { single:  true } );
20137             
20138             return true;
20139         }
20140         
20141         cur.setActive(false);
20142         pan.setActive(true);
20143         
20144         return true;
20145         
20146     },
20147     showPanelNext : function()
20148     {
20149         var i = this.indexOfPanel(this.getActivePanel());
20150         
20151         if (i >= this.tabs.length - 1 && !this.autoslide) {
20152             return;
20153         }
20154         
20155         if (i >= this.tabs.length - 1 && this.autoslide) {
20156             i = -1;
20157         }
20158         
20159         this.showPanel(this.tabs[i+1]);
20160     },
20161     
20162     showPanelPrev : function()
20163     {
20164         var i = this.indexOfPanel(this.getActivePanel());
20165         
20166         if (i  < 1 && !this.autoslide) {
20167             return;
20168         }
20169         
20170         if (i < 1 && this.autoslide) {
20171             i = this.tabs.length;
20172         }
20173         
20174         this.showPanel(this.tabs[i-1]);
20175     },
20176     
20177     
20178     addBullet: function()
20179     {
20180         if(!this.bullets || Roo.isTouch){
20181             return;
20182         }
20183         var ctr = this.el.select('.carousel-bullets',true).first();
20184         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20185         var bullet = ctr.createChild({
20186             cls : 'bullet bullet-' + i
20187         },ctr.dom.lastChild);
20188         
20189         
20190         var _this = this;
20191         
20192         bullet.on('click', (function(e, el, o, ii, t){
20193
20194             e.preventDefault();
20195
20196             this.showPanel(ii);
20197
20198             if(this.autoslide && this.slideFn){
20199                 clearInterval(this.slideFn);
20200                 this.slideFn = window.setInterval(function() {
20201                     _this.showPanelNext();
20202                 }, this.timer);
20203             }
20204
20205         }).createDelegate(this, [i, bullet], true));
20206                 
20207         
20208     },
20209      
20210     setActiveBullet : function(i)
20211     {
20212         if(Roo.isTouch){
20213             return;
20214         }
20215         
20216         Roo.each(this.el.select('.bullet', true).elements, function(el){
20217             el.removeClass('selected');
20218         });
20219
20220         var bullet = this.el.select('.bullet-' + i, true).first();
20221         
20222         if(!bullet){
20223             return;
20224         }
20225         
20226         bullet.addClass('selected');
20227     }
20228     
20229     
20230   
20231 });
20232
20233  
20234
20235  
20236  
20237 Roo.apply(Roo.bootstrap.TabGroup, {
20238     
20239     groups: {},
20240      /**
20241     * register a Navigation Group
20242     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20243     */
20244     register : function(navgrp)
20245     {
20246         this.groups[navgrp.navId] = navgrp;
20247         
20248     },
20249     /**
20250     * fetch a Navigation Group based on the navigation ID
20251     * if one does not exist , it will get created.
20252     * @param {string} the navgroup to add
20253     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20254     */
20255     get: function(navId) {
20256         if (typeof(this.groups[navId]) == 'undefined') {
20257             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20258         }
20259         return this.groups[navId] ;
20260     }
20261     
20262     
20263     
20264 });
20265
20266  /*
20267  * - LGPL
20268  *
20269  * TabPanel
20270  * 
20271  */
20272
20273 /**
20274  * @class Roo.bootstrap.TabPanel
20275  * @extends Roo.bootstrap.Component
20276  * Bootstrap TabPanel class
20277  * @cfg {Boolean} active panel active
20278  * @cfg {String} html panel content
20279  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20280  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20281  * @cfg {String} href click to link..
20282  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20283  * 
20284  * 
20285  * @constructor
20286  * Create a new TabPanel
20287  * @param {Object} config The config object
20288  */
20289
20290 Roo.bootstrap.TabPanel = function(config){
20291     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20292     this.addEvents({
20293         /**
20294              * @event changed
20295              * Fires when the active status changes
20296              * @param {Roo.bootstrap.TabPanel} this
20297              * @param {Boolean} state the new state
20298             
20299          */
20300         'changed': true,
20301         /**
20302              * @event beforedeactivate
20303              * Fires before a tab is de-activated - can be used to do validation on a form.
20304              * @param {Roo.bootstrap.TabPanel} this
20305              * @return {Boolean} false if there is an error
20306             
20307          */
20308         'beforedeactivate': true
20309      });
20310     
20311     this.tabId = this.tabId || Roo.id();
20312   
20313 };
20314
20315 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20316     
20317     active: false,
20318     html: false,
20319     tabId: false,
20320     navId : false,
20321     href : '',
20322     touchSlide : false,
20323     getAutoCreate : function(){
20324         
20325         
20326         var cfg = {
20327             tag: 'div',
20328             // item is needed for carousel - not sure if it has any effect otherwise
20329             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20330             html: this.html || ''
20331         };
20332         
20333         if(this.active){
20334             cfg.cls += ' active';
20335         }
20336         
20337         if(this.tabId){
20338             cfg.tabId = this.tabId;
20339         }
20340         
20341         
20342         
20343         return cfg;
20344     },
20345     
20346     initEvents:  function()
20347     {
20348         var p = this.parent();
20349         
20350         this.navId = this.navId || p.navId;
20351         
20352         if (typeof(this.navId) != 'undefined') {
20353             // not really needed.. but just in case.. parent should be a NavGroup.
20354             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20355             
20356             tg.register(this);
20357             
20358             var i = tg.tabs.length - 1;
20359             
20360             if(this.active && tg.bullets > 0 && i < tg.bullets){
20361                 tg.setActiveBullet(i);
20362             }
20363         }
20364         
20365         this.el.on('click', this.onClick, this);
20366         
20367         if(Roo.isTouch && this.touchSlide){
20368             this.el.on("touchstart", this.onTouchStart, this);
20369             this.el.on("touchmove", this.onTouchMove, this);
20370             this.el.on("touchend", this.onTouchEnd, this);
20371         }
20372         
20373     },
20374     
20375     onRender : function(ct, position)
20376     {
20377         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20378     },
20379     
20380     setActive : function(state)
20381     {
20382         Roo.log("panel - set active " + this.tabId + "=" + state);
20383         
20384         this.active = state;
20385         if (!state) {
20386             this.el.removeClass('active');
20387             
20388         } else  if (!this.el.hasClass('active')) {
20389             this.el.addClass('active');
20390         }
20391         
20392         this.fireEvent('changed', this, state);
20393     },
20394     
20395     onClick : function(e)
20396     {
20397         e.preventDefault();
20398         
20399         if(!this.href.length){
20400             return;
20401         }
20402         
20403         window.location.href = this.href;
20404     },
20405     
20406     startX : 0,
20407     startY : 0,
20408     endX : 0,
20409     endY : 0,
20410     swiping : false,
20411     
20412     onTouchStart : function(e)
20413     {
20414         this.swiping = false;
20415         
20416         this.startX = e.browserEvent.touches[0].clientX;
20417         this.startY = e.browserEvent.touches[0].clientY;
20418     },
20419     
20420     onTouchMove : function(e)
20421     {
20422         this.swiping = true;
20423         
20424         this.endX = e.browserEvent.touches[0].clientX;
20425         this.endY = e.browserEvent.touches[0].clientY;
20426     },
20427     
20428     onTouchEnd : function(e)
20429     {
20430         if(!this.swiping){
20431             this.onClick(e);
20432             return;
20433         }
20434         
20435         var tabGroup = this.parent();
20436         
20437         if(this.endX > this.startX){ // swiping right
20438             tabGroup.showPanelPrev();
20439             return;
20440         }
20441         
20442         if(this.startX > this.endX){ // swiping left
20443             tabGroup.showPanelNext();
20444             return;
20445         }
20446     }
20447     
20448     
20449 });
20450  
20451
20452  
20453
20454  /*
20455  * - LGPL
20456  *
20457  * DateField
20458  * 
20459  */
20460
20461 /**
20462  * @class Roo.bootstrap.DateField
20463  * @extends Roo.bootstrap.Input
20464  * Bootstrap DateField class
20465  * @cfg {Number} weekStart default 0
20466  * @cfg {String} viewMode default empty, (months|years)
20467  * @cfg {String} minViewMode default empty, (months|years)
20468  * @cfg {Number} startDate default -Infinity
20469  * @cfg {Number} endDate default Infinity
20470  * @cfg {Boolean} todayHighlight default false
20471  * @cfg {Boolean} todayBtn default false
20472  * @cfg {Boolean} calendarWeeks default false
20473  * @cfg {Object} daysOfWeekDisabled default empty
20474  * @cfg {Boolean} singleMode default false (true | false)
20475  * 
20476  * @cfg {Boolean} keyboardNavigation default true
20477  * @cfg {String} language default en
20478  * 
20479  * @constructor
20480  * Create a new DateField
20481  * @param {Object} config The config object
20482  */
20483
20484 Roo.bootstrap.DateField = function(config){
20485     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20486      this.addEvents({
20487             /**
20488              * @event show
20489              * Fires when this field show.
20490              * @param {Roo.bootstrap.DateField} this
20491              * @param {Mixed} date The date value
20492              */
20493             show : true,
20494             /**
20495              * @event show
20496              * Fires when this field hide.
20497              * @param {Roo.bootstrap.DateField} this
20498              * @param {Mixed} date The date value
20499              */
20500             hide : true,
20501             /**
20502              * @event select
20503              * Fires when select a date.
20504              * @param {Roo.bootstrap.DateField} this
20505              * @param {Mixed} date The date value
20506              */
20507             select : true,
20508             /**
20509              * @event beforeselect
20510              * Fires when before select a date.
20511              * @param {Roo.bootstrap.DateField} this
20512              * @param {Mixed} date The date value
20513              */
20514             beforeselect : true
20515         });
20516 };
20517
20518 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20519     
20520     /**
20521      * @cfg {String} format
20522      * The default date format string which can be overriden for localization support.  The format must be
20523      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20524      */
20525     format : "m/d/y",
20526     /**
20527      * @cfg {String} altFormats
20528      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20529      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20530      */
20531     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20532     
20533     weekStart : 0,
20534     
20535     viewMode : '',
20536     
20537     minViewMode : '',
20538     
20539     todayHighlight : false,
20540     
20541     todayBtn: false,
20542     
20543     language: 'en',
20544     
20545     keyboardNavigation: true,
20546     
20547     calendarWeeks: false,
20548     
20549     startDate: -Infinity,
20550     
20551     endDate: Infinity,
20552     
20553     daysOfWeekDisabled: [],
20554     
20555     _events: [],
20556     
20557     singleMode : false,
20558     
20559     UTCDate: function()
20560     {
20561         return new Date(Date.UTC.apply(Date, arguments));
20562     },
20563     
20564     UTCToday: function()
20565     {
20566         var today = new Date();
20567         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20568     },
20569     
20570     getDate: function() {
20571             var d = this.getUTCDate();
20572             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20573     },
20574     
20575     getUTCDate: function() {
20576             return this.date;
20577     },
20578     
20579     setDate: function(d) {
20580             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20581     },
20582     
20583     setUTCDate: function(d) {
20584             this.date = d;
20585             this.setValue(this.formatDate(this.date));
20586     },
20587         
20588     onRender: function(ct, position)
20589     {
20590         
20591         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20592         
20593         this.language = this.language || 'en';
20594         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20595         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20596         
20597         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20598         this.format = this.format || 'm/d/y';
20599         this.isInline = false;
20600         this.isInput = true;
20601         this.component = this.el.select('.add-on', true).first() || false;
20602         this.component = (this.component && this.component.length === 0) ? false : this.component;
20603         this.hasInput = this.component && this.inputEl().length;
20604         
20605         if (typeof(this.minViewMode === 'string')) {
20606             switch (this.minViewMode) {
20607                 case 'months':
20608                     this.minViewMode = 1;
20609                     break;
20610                 case 'years':
20611                     this.minViewMode = 2;
20612                     break;
20613                 default:
20614                     this.minViewMode = 0;
20615                     break;
20616             }
20617         }
20618         
20619         if (typeof(this.viewMode === 'string')) {
20620             switch (this.viewMode) {
20621                 case 'months':
20622                     this.viewMode = 1;
20623                     break;
20624                 case 'years':
20625                     this.viewMode = 2;
20626                     break;
20627                 default:
20628                     this.viewMode = 0;
20629                     break;
20630             }
20631         }
20632                 
20633         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20634         
20635 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20636         
20637         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20638         
20639         this.picker().on('mousedown', this.onMousedown, this);
20640         this.picker().on('click', this.onClick, this);
20641         
20642         this.picker().addClass('datepicker-dropdown');
20643         
20644         this.startViewMode = this.viewMode;
20645         
20646         if(this.singleMode){
20647             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20648                 v.setVisibilityMode(Roo.Element.DISPLAY);
20649                 v.hide();
20650             });
20651             
20652             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20653                 v.setStyle('width', '189px');
20654             });
20655         }
20656         
20657         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20658             if(!this.calendarWeeks){
20659                 v.remove();
20660                 return;
20661             }
20662             
20663             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20664             v.attr('colspan', function(i, val){
20665                 return parseInt(val) + 1;
20666             });
20667         });
20668                         
20669         
20670         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20671         
20672         this.setStartDate(this.startDate);
20673         this.setEndDate(this.endDate);
20674         
20675         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20676         
20677         this.fillDow();
20678         this.fillMonths();
20679         this.update();
20680         this.showMode();
20681         
20682         if(this.isInline) {
20683             this.showPopup();
20684         }
20685     },
20686     
20687     picker : function()
20688     {
20689         return this.pickerEl;
20690 //        return this.el.select('.datepicker', true).first();
20691     },
20692     
20693     fillDow: function()
20694     {
20695         var dowCnt = this.weekStart;
20696         
20697         var dow = {
20698             tag: 'tr',
20699             cn: [
20700                 
20701             ]
20702         };
20703         
20704         if(this.calendarWeeks){
20705             dow.cn.push({
20706                 tag: 'th',
20707                 cls: 'cw',
20708                 html: '&nbsp;'
20709             })
20710         }
20711         
20712         while (dowCnt < this.weekStart + 7) {
20713             dow.cn.push({
20714                 tag: 'th',
20715                 cls: 'dow',
20716                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20717             });
20718         }
20719         
20720         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20721     },
20722     
20723     fillMonths: function()
20724     {    
20725         var i = 0;
20726         var months = this.picker().select('>.datepicker-months td', true).first();
20727         
20728         months.dom.innerHTML = '';
20729         
20730         while (i < 12) {
20731             var month = {
20732                 tag: 'span',
20733                 cls: 'month',
20734                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20735             };
20736             
20737             months.createChild(month);
20738         }
20739         
20740     },
20741     
20742     update: function()
20743     {
20744         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;
20745         
20746         if (this.date < this.startDate) {
20747             this.viewDate = new Date(this.startDate);
20748         } else if (this.date > this.endDate) {
20749             this.viewDate = new Date(this.endDate);
20750         } else {
20751             this.viewDate = new Date(this.date);
20752         }
20753         
20754         this.fill();
20755     },
20756     
20757     fill: function() 
20758     {
20759         var d = new Date(this.viewDate),
20760                 year = d.getUTCFullYear(),
20761                 month = d.getUTCMonth(),
20762                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20763                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20764                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20765                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20766                 currentDate = this.date && this.date.valueOf(),
20767                 today = this.UTCToday();
20768         
20769         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20770         
20771 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20772         
20773 //        this.picker.select('>tfoot th.today').
20774 //                                              .text(dates[this.language].today)
20775 //                                              .toggle(this.todayBtn !== false);
20776     
20777         this.updateNavArrows();
20778         this.fillMonths();
20779                                                 
20780         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20781         
20782         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20783          
20784         prevMonth.setUTCDate(day);
20785         
20786         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20787         
20788         var nextMonth = new Date(prevMonth);
20789         
20790         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20791         
20792         nextMonth = nextMonth.valueOf();
20793         
20794         var fillMonths = false;
20795         
20796         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20797         
20798         while(prevMonth.valueOf() <= nextMonth) {
20799             var clsName = '';
20800             
20801             if (prevMonth.getUTCDay() === this.weekStart) {
20802                 if(fillMonths){
20803                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20804                 }
20805                     
20806                 fillMonths = {
20807                     tag: 'tr',
20808                     cn: []
20809                 };
20810                 
20811                 if(this.calendarWeeks){
20812                     // ISO 8601: First week contains first thursday.
20813                     // ISO also states week starts on Monday, but we can be more abstract here.
20814                     var
20815                     // Start of current week: based on weekstart/current date
20816                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20817                     // Thursday of this week
20818                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20819                     // First Thursday of year, year from thursday
20820                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20821                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20822                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20823                     
20824                     fillMonths.cn.push({
20825                         tag: 'td',
20826                         cls: 'cw',
20827                         html: calWeek
20828                     });
20829                 }
20830             }
20831             
20832             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20833                 clsName += ' old';
20834             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20835                 clsName += ' new';
20836             }
20837             if (this.todayHighlight &&
20838                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20839                 prevMonth.getUTCMonth() == today.getMonth() &&
20840                 prevMonth.getUTCDate() == today.getDate()) {
20841                 clsName += ' today';
20842             }
20843             
20844             if (currentDate && prevMonth.valueOf() === currentDate) {
20845                 clsName += ' active';
20846             }
20847             
20848             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20849                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20850                     clsName += ' disabled';
20851             }
20852             
20853             fillMonths.cn.push({
20854                 tag: 'td',
20855                 cls: 'day ' + clsName,
20856                 html: prevMonth.getDate()
20857             });
20858             
20859             prevMonth.setDate(prevMonth.getDate()+1);
20860         }
20861           
20862         var currentYear = this.date && this.date.getUTCFullYear();
20863         var currentMonth = this.date && this.date.getUTCMonth();
20864         
20865         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20866         
20867         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20868             v.removeClass('active');
20869             
20870             if(currentYear === year && k === currentMonth){
20871                 v.addClass('active');
20872             }
20873             
20874             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20875                 v.addClass('disabled');
20876             }
20877             
20878         });
20879         
20880         
20881         year = parseInt(year/10, 10) * 10;
20882         
20883         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20884         
20885         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20886         
20887         year -= 1;
20888         for (var i = -1; i < 11; i++) {
20889             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20890                 tag: 'span',
20891                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20892                 html: year
20893             });
20894             
20895             year += 1;
20896         }
20897     },
20898     
20899     showMode: function(dir) 
20900     {
20901         if (dir) {
20902             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20903         }
20904         
20905         Roo.each(this.picker().select('>div',true).elements, function(v){
20906             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20907             v.hide();
20908         });
20909         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20910     },
20911     
20912     place: function()
20913     {
20914         if(this.isInline) {
20915             return;
20916         }
20917         
20918         this.picker().removeClass(['bottom', 'top']);
20919         
20920         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20921             /*
20922              * place to the top of element!
20923              *
20924              */
20925             
20926             this.picker().addClass('top');
20927             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20928             
20929             return;
20930         }
20931         
20932         this.picker().addClass('bottom');
20933         
20934         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20935     },
20936     
20937     parseDate : function(value)
20938     {
20939         if(!value || value instanceof Date){
20940             return value;
20941         }
20942         var v = Date.parseDate(value, this.format);
20943         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20944             v = Date.parseDate(value, 'Y-m-d');
20945         }
20946         if(!v && this.altFormats){
20947             if(!this.altFormatsArray){
20948                 this.altFormatsArray = this.altFormats.split("|");
20949             }
20950             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20951                 v = Date.parseDate(value, this.altFormatsArray[i]);
20952             }
20953         }
20954         return v;
20955     },
20956     
20957     formatDate : function(date, fmt)
20958     {   
20959         return (!date || !(date instanceof Date)) ?
20960         date : date.dateFormat(fmt || this.format);
20961     },
20962     
20963     onFocus : function()
20964     {
20965         Roo.bootstrap.DateField.superclass.onFocus.call(this);
20966         this.showPopup();
20967     },
20968     
20969     onBlur : function()
20970     {
20971         Roo.bootstrap.DateField.superclass.onBlur.call(this);
20972         
20973         var d = this.inputEl().getValue();
20974         
20975         this.setValue(d);
20976                 
20977         this.hidePopup();
20978     },
20979     
20980     showPopup : function()
20981     {
20982         this.picker().show();
20983         this.update();
20984         this.place();
20985         
20986         this.fireEvent('showpopup', this, this.date);
20987     },
20988     
20989     hidePopup : function()
20990     {
20991         if(this.isInline) {
20992             return;
20993         }
20994         this.picker().hide();
20995         this.viewMode = this.startViewMode;
20996         this.showMode();
20997         
20998         this.fireEvent('hidepopup', this, this.date);
20999         
21000     },
21001     
21002     onMousedown: function(e)
21003     {
21004         e.stopPropagation();
21005         e.preventDefault();
21006     },
21007     
21008     keyup: function(e)
21009     {
21010         Roo.bootstrap.DateField.superclass.keyup.call(this);
21011         this.update();
21012     },
21013
21014     setValue: function(v)
21015     {
21016         if(this.fireEvent('beforeselect', this, v) !== false){
21017             var d = new Date(this.parseDate(v) ).clearTime();
21018         
21019             if(isNaN(d.getTime())){
21020                 this.date = this.viewDate = '';
21021                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21022                 return;
21023             }
21024
21025             v = this.formatDate(d);
21026
21027             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21028
21029             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21030
21031             this.update();
21032
21033             this.fireEvent('select', this, this.date);
21034         }
21035     },
21036     
21037     getValue: function()
21038     {
21039         return this.formatDate(this.date);
21040     },
21041     
21042     fireKey: function(e)
21043     {
21044         if (!this.picker().isVisible()){
21045             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21046                 this.showPopup();
21047             }
21048             return;
21049         }
21050         
21051         var dateChanged = false,
21052         dir, day, month,
21053         newDate, newViewDate;
21054         
21055         switch(e.keyCode){
21056             case 27: // escape
21057                 this.hidePopup();
21058                 e.preventDefault();
21059                 break;
21060             case 37: // left
21061             case 39: // right
21062                 if (!this.keyboardNavigation) {
21063                     break;
21064                 }
21065                 dir = e.keyCode == 37 ? -1 : 1;
21066                 
21067                 if (e.ctrlKey){
21068                     newDate = this.moveYear(this.date, dir);
21069                     newViewDate = this.moveYear(this.viewDate, dir);
21070                 } else if (e.shiftKey){
21071                     newDate = this.moveMonth(this.date, dir);
21072                     newViewDate = this.moveMonth(this.viewDate, dir);
21073                 } else {
21074                     newDate = new Date(this.date);
21075                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21076                     newViewDate = new Date(this.viewDate);
21077                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21078                 }
21079                 if (this.dateWithinRange(newDate)){
21080                     this.date = newDate;
21081                     this.viewDate = newViewDate;
21082                     this.setValue(this.formatDate(this.date));
21083 //                    this.update();
21084                     e.preventDefault();
21085                     dateChanged = true;
21086                 }
21087                 break;
21088             case 38: // up
21089             case 40: // down
21090                 if (!this.keyboardNavigation) {
21091                     break;
21092                 }
21093                 dir = e.keyCode == 38 ? -1 : 1;
21094                 if (e.ctrlKey){
21095                     newDate = this.moveYear(this.date, dir);
21096                     newViewDate = this.moveYear(this.viewDate, dir);
21097                 } else if (e.shiftKey){
21098                     newDate = this.moveMonth(this.date, dir);
21099                     newViewDate = this.moveMonth(this.viewDate, dir);
21100                 } else {
21101                     newDate = new Date(this.date);
21102                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21103                     newViewDate = new Date(this.viewDate);
21104                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21105                 }
21106                 if (this.dateWithinRange(newDate)){
21107                     this.date = newDate;
21108                     this.viewDate = newViewDate;
21109                     this.setValue(this.formatDate(this.date));
21110 //                    this.update();
21111                     e.preventDefault();
21112                     dateChanged = true;
21113                 }
21114                 break;
21115             case 13: // enter
21116                 this.setValue(this.formatDate(this.date));
21117                 this.hidePopup();
21118                 e.preventDefault();
21119                 break;
21120             case 9: // tab
21121                 this.setValue(this.formatDate(this.date));
21122                 this.hidePopup();
21123                 break;
21124             case 16: // shift
21125             case 17: // ctrl
21126             case 18: // alt
21127                 break;
21128             default :
21129                 this.hidePopup();
21130                 
21131         }
21132     },
21133     
21134     
21135     onClick: function(e) 
21136     {
21137         e.stopPropagation();
21138         e.preventDefault();
21139         
21140         var target = e.getTarget();
21141         
21142         if(target.nodeName.toLowerCase() === 'i'){
21143             target = Roo.get(target).dom.parentNode;
21144         }
21145         
21146         var nodeName = target.nodeName;
21147         var className = target.className;
21148         var html = target.innerHTML;
21149         //Roo.log(nodeName);
21150         
21151         switch(nodeName.toLowerCase()) {
21152             case 'th':
21153                 switch(className) {
21154                     case 'switch':
21155                         this.showMode(1);
21156                         break;
21157                     case 'prev':
21158                     case 'next':
21159                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21160                         switch(this.viewMode){
21161                                 case 0:
21162                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21163                                         break;
21164                                 case 1:
21165                                 case 2:
21166                                         this.viewDate = this.moveYear(this.viewDate, dir);
21167                                         break;
21168                         }
21169                         this.fill();
21170                         break;
21171                     case 'today':
21172                         var date = new Date();
21173                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21174 //                        this.fill()
21175                         this.setValue(this.formatDate(this.date));
21176                         
21177                         this.hidePopup();
21178                         break;
21179                 }
21180                 break;
21181             case 'span':
21182                 if (className.indexOf('disabled') < 0) {
21183                     this.viewDate.setUTCDate(1);
21184                     if (className.indexOf('month') > -1) {
21185                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21186                     } else {
21187                         var year = parseInt(html, 10) || 0;
21188                         this.viewDate.setUTCFullYear(year);
21189                         
21190                     }
21191                     
21192                     if(this.singleMode){
21193                         this.setValue(this.formatDate(this.viewDate));
21194                         this.hidePopup();
21195                         return;
21196                     }
21197                     
21198                     this.showMode(-1);
21199                     this.fill();
21200                 }
21201                 break;
21202                 
21203             case 'td':
21204                 //Roo.log(className);
21205                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21206                     var day = parseInt(html, 10) || 1;
21207                     var year = this.viewDate.getUTCFullYear(),
21208                         month = this.viewDate.getUTCMonth();
21209
21210                     if (className.indexOf('old') > -1) {
21211                         if(month === 0 ){
21212                             month = 11;
21213                             year -= 1;
21214                         }else{
21215                             month -= 1;
21216                         }
21217                     } else if (className.indexOf('new') > -1) {
21218                         if (month == 11) {
21219                             month = 0;
21220                             year += 1;
21221                         } else {
21222                             month += 1;
21223                         }
21224                     }
21225                     //Roo.log([year,month,day]);
21226                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21227                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21228 //                    this.fill();
21229                     //Roo.log(this.formatDate(this.date));
21230                     this.setValue(this.formatDate(this.date));
21231                     this.hidePopup();
21232                 }
21233                 break;
21234         }
21235     },
21236     
21237     setStartDate: function(startDate)
21238     {
21239         this.startDate = startDate || -Infinity;
21240         if (this.startDate !== -Infinity) {
21241             this.startDate = this.parseDate(this.startDate);
21242         }
21243         this.update();
21244         this.updateNavArrows();
21245     },
21246
21247     setEndDate: function(endDate)
21248     {
21249         this.endDate = endDate || Infinity;
21250         if (this.endDate !== Infinity) {
21251             this.endDate = this.parseDate(this.endDate);
21252         }
21253         this.update();
21254         this.updateNavArrows();
21255     },
21256     
21257     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21258     {
21259         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21260         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21261             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21262         }
21263         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21264             return parseInt(d, 10);
21265         });
21266         this.update();
21267         this.updateNavArrows();
21268     },
21269     
21270     updateNavArrows: function() 
21271     {
21272         if(this.singleMode){
21273             return;
21274         }
21275         
21276         var d = new Date(this.viewDate),
21277         year = d.getUTCFullYear(),
21278         month = d.getUTCMonth();
21279         
21280         Roo.each(this.picker().select('.prev', true).elements, function(v){
21281             v.show();
21282             switch (this.viewMode) {
21283                 case 0:
21284
21285                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21286                         v.hide();
21287                     }
21288                     break;
21289                 case 1:
21290                 case 2:
21291                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21292                         v.hide();
21293                     }
21294                     break;
21295             }
21296         });
21297         
21298         Roo.each(this.picker().select('.next', true).elements, function(v){
21299             v.show();
21300             switch (this.viewMode) {
21301                 case 0:
21302
21303                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21304                         v.hide();
21305                     }
21306                     break;
21307                 case 1:
21308                 case 2:
21309                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21310                         v.hide();
21311                     }
21312                     break;
21313             }
21314         })
21315     },
21316     
21317     moveMonth: function(date, dir)
21318     {
21319         if (!dir) {
21320             return date;
21321         }
21322         var new_date = new Date(date.valueOf()),
21323         day = new_date.getUTCDate(),
21324         month = new_date.getUTCMonth(),
21325         mag = Math.abs(dir),
21326         new_month, test;
21327         dir = dir > 0 ? 1 : -1;
21328         if (mag == 1){
21329             test = dir == -1
21330             // If going back one month, make sure month is not current month
21331             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21332             ? function(){
21333                 return new_date.getUTCMonth() == month;
21334             }
21335             // If going forward one month, make sure month is as expected
21336             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21337             : function(){
21338                 return new_date.getUTCMonth() != new_month;
21339             };
21340             new_month = month + dir;
21341             new_date.setUTCMonth(new_month);
21342             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21343             if (new_month < 0 || new_month > 11) {
21344                 new_month = (new_month + 12) % 12;
21345             }
21346         } else {
21347             // For magnitudes >1, move one month at a time...
21348             for (var i=0; i<mag; i++) {
21349                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21350                 new_date = this.moveMonth(new_date, dir);
21351             }
21352             // ...then reset the day, keeping it in the new month
21353             new_month = new_date.getUTCMonth();
21354             new_date.setUTCDate(day);
21355             test = function(){
21356                 return new_month != new_date.getUTCMonth();
21357             };
21358         }
21359         // Common date-resetting loop -- if date is beyond end of month, make it
21360         // end of month
21361         while (test()){
21362             new_date.setUTCDate(--day);
21363             new_date.setUTCMonth(new_month);
21364         }
21365         return new_date;
21366     },
21367
21368     moveYear: function(date, dir)
21369     {
21370         return this.moveMonth(date, dir*12);
21371     },
21372
21373     dateWithinRange: function(date)
21374     {
21375         return date >= this.startDate && date <= this.endDate;
21376     },
21377
21378     
21379     remove: function() 
21380     {
21381         this.picker().remove();
21382     },
21383     
21384     validateValue : function(value)
21385     {
21386         if(this.getVisibilityEl().hasClass('hidden')){
21387             return true;
21388         }
21389         
21390         if(value.length < 1)  {
21391             if(this.allowBlank){
21392                 return true;
21393             }
21394             return false;
21395         }
21396         
21397         if(value.length < this.minLength){
21398             return false;
21399         }
21400         if(value.length > this.maxLength){
21401             return false;
21402         }
21403         if(this.vtype){
21404             var vt = Roo.form.VTypes;
21405             if(!vt[this.vtype](value, this)){
21406                 return false;
21407             }
21408         }
21409         if(typeof this.validator == "function"){
21410             var msg = this.validator(value);
21411             if(msg !== true){
21412                 return false;
21413             }
21414         }
21415         
21416         if(this.regex && !this.regex.test(value)){
21417             return false;
21418         }
21419         
21420         if(typeof(this.parseDate(value)) == 'undefined'){
21421             return false;
21422         }
21423         
21424         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21425             return false;
21426         }      
21427         
21428         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21429             return false;
21430         } 
21431         
21432         
21433         return true;
21434     },
21435     
21436     reset : function()
21437     {
21438         this.date = this.viewDate = '';
21439         
21440         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21441     }
21442    
21443 });
21444
21445 Roo.apply(Roo.bootstrap.DateField,  {
21446     
21447     head : {
21448         tag: 'thead',
21449         cn: [
21450         {
21451             tag: 'tr',
21452             cn: [
21453             {
21454                 tag: 'th',
21455                 cls: 'prev',
21456                 html: '<i class="fa fa-arrow-left"/>'
21457             },
21458             {
21459                 tag: 'th',
21460                 cls: 'switch',
21461                 colspan: '5'
21462             },
21463             {
21464                 tag: 'th',
21465                 cls: 'next',
21466                 html: '<i class="fa fa-arrow-right"/>'
21467             }
21468
21469             ]
21470         }
21471         ]
21472     },
21473     
21474     content : {
21475         tag: 'tbody',
21476         cn: [
21477         {
21478             tag: 'tr',
21479             cn: [
21480             {
21481                 tag: 'td',
21482                 colspan: '7'
21483             }
21484             ]
21485         }
21486         ]
21487     },
21488     
21489     footer : {
21490         tag: 'tfoot',
21491         cn: [
21492         {
21493             tag: 'tr',
21494             cn: [
21495             {
21496                 tag: 'th',
21497                 colspan: '7',
21498                 cls: 'today'
21499             }
21500                     
21501             ]
21502         }
21503         ]
21504     },
21505     
21506     dates:{
21507         en: {
21508             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21509             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21510             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21511             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21512             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21513             today: "Today"
21514         }
21515     },
21516     
21517     modes: [
21518     {
21519         clsName: 'days',
21520         navFnc: 'Month',
21521         navStep: 1
21522     },
21523     {
21524         clsName: 'months',
21525         navFnc: 'FullYear',
21526         navStep: 1
21527     },
21528     {
21529         clsName: 'years',
21530         navFnc: 'FullYear',
21531         navStep: 10
21532     }]
21533 });
21534
21535 Roo.apply(Roo.bootstrap.DateField,  {
21536   
21537     template : {
21538         tag: 'div',
21539         cls: 'datepicker dropdown-menu roo-dynamic',
21540         cn: [
21541         {
21542             tag: 'div',
21543             cls: 'datepicker-days',
21544             cn: [
21545             {
21546                 tag: 'table',
21547                 cls: 'table-condensed',
21548                 cn:[
21549                 Roo.bootstrap.DateField.head,
21550                 {
21551                     tag: 'tbody'
21552                 },
21553                 Roo.bootstrap.DateField.footer
21554                 ]
21555             }
21556             ]
21557         },
21558         {
21559             tag: 'div',
21560             cls: 'datepicker-months',
21561             cn: [
21562             {
21563                 tag: 'table',
21564                 cls: 'table-condensed',
21565                 cn:[
21566                 Roo.bootstrap.DateField.head,
21567                 Roo.bootstrap.DateField.content,
21568                 Roo.bootstrap.DateField.footer
21569                 ]
21570             }
21571             ]
21572         },
21573         {
21574             tag: 'div',
21575             cls: 'datepicker-years',
21576             cn: [
21577             {
21578                 tag: 'table',
21579                 cls: 'table-condensed',
21580                 cn:[
21581                 Roo.bootstrap.DateField.head,
21582                 Roo.bootstrap.DateField.content,
21583                 Roo.bootstrap.DateField.footer
21584                 ]
21585             }
21586             ]
21587         }
21588         ]
21589     }
21590 });
21591
21592  
21593
21594  /*
21595  * - LGPL
21596  *
21597  * TimeField
21598  * 
21599  */
21600
21601 /**
21602  * @class Roo.bootstrap.TimeField
21603  * @extends Roo.bootstrap.Input
21604  * Bootstrap DateField class
21605  * 
21606  * 
21607  * @constructor
21608  * Create a new TimeField
21609  * @param {Object} config The config object
21610  */
21611
21612 Roo.bootstrap.TimeField = function(config){
21613     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21614     this.addEvents({
21615             /**
21616              * @event show
21617              * Fires when this field show.
21618              * @param {Roo.bootstrap.DateField} thisthis
21619              * @param {Mixed} date The date value
21620              */
21621             show : true,
21622             /**
21623              * @event show
21624              * Fires when this field hide.
21625              * @param {Roo.bootstrap.DateField} this
21626              * @param {Mixed} date The date value
21627              */
21628             hide : true,
21629             /**
21630              * @event select
21631              * Fires when select a date.
21632              * @param {Roo.bootstrap.DateField} this
21633              * @param {Mixed} date The date value
21634              */
21635             select : true
21636         });
21637 };
21638
21639 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21640     
21641     /**
21642      * @cfg {String} format
21643      * The default time format string which can be overriden for localization support.  The format must be
21644      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21645      */
21646     format : "H:i",
21647        
21648     onRender: function(ct, position)
21649     {
21650         
21651         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21652                 
21653         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21654         
21655         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21656         
21657         this.pop = this.picker().select('>.datepicker-time',true).first();
21658         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21659         
21660         this.picker().on('mousedown', this.onMousedown, this);
21661         this.picker().on('click', this.onClick, this);
21662         
21663         this.picker().addClass('datepicker-dropdown');
21664     
21665         this.fillTime();
21666         this.update();
21667             
21668         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21669         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21670         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21671         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21672         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21673         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21674
21675     },
21676     
21677     fireKey: function(e){
21678         if (!this.picker().isVisible()){
21679             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21680                 this.show();
21681             }
21682             return;
21683         }
21684
21685         e.preventDefault();
21686         
21687         switch(e.keyCode){
21688             case 27: // escape
21689                 this.hide();
21690                 break;
21691             case 37: // left
21692             case 39: // right
21693                 this.onTogglePeriod();
21694                 break;
21695             case 38: // up
21696                 this.onIncrementMinutes();
21697                 break;
21698             case 40: // down
21699                 this.onDecrementMinutes();
21700                 break;
21701             case 13: // enter
21702             case 9: // tab
21703                 this.setTime();
21704                 break;
21705         }
21706     },
21707     
21708     onClick: function(e) {
21709         e.stopPropagation();
21710         e.preventDefault();
21711     },
21712     
21713     picker : function()
21714     {
21715         return this.el.select('.datepicker', true).first();
21716     },
21717     
21718     fillTime: function()
21719     {    
21720         var time = this.pop.select('tbody', true).first();
21721         
21722         time.dom.innerHTML = '';
21723         
21724         time.createChild({
21725             tag: 'tr',
21726             cn: [
21727                 {
21728                     tag: 'td',
21729                     cn: [
21730                         {
21731                             tag: 'a',
21732                             href: '#',
21733                             cls: 'btn',
21734                             cn: [
21735                                 {
21736                                     tag: 'span',
21737                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21738                                 }
21739                             ]
21740                         } 
21741                     ]
21742                 },
21743                 {
21744                     tag: 'td',
21745                     cls: 'separator'
21746                 },
21747                 {
21748                     tag: 'td',
21749                     cn: [
21750                         {
21751                             tag: 'a',
21752                             href: '#',
21753                             cls: 'btn',
21754                             cn: [
21755                                 {
21756                                     tag: 'span',
21757                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21758                                 }
21759                             ]
21760                         }
21761                     ]
21762                 },
21763                 {
21764                     tag: 'td',
21765                     cls: 'separator'
21766                 }
21767             ]
21768         });
21769         
21770         time.createChild({
21771             tag: 'tr',
21772             cn: [
21773                 {
21774                     tag: 'td',
21775                     cn: [
21776                         {
21777                             tag: 'span',
21778                             cls: 'timepicker-hour',
21779                             html: '00'
21780                         }  
21781                     ]
21782                 },
21783                 {
21784                     tag: 'td',
21785                     cls: 'separator',
21786                     html: ':'
21787                 },
21788                 {
21789                     tag: 'td',
21790                     cn: [
21791                         {
21792                             tag: 'span',
21793                             cls: 'timepicker-minute',
21794                             html: '00'
21795                         }  
21796                     ]
21797                 },
21798                 {
21799                     tag: 'td',
21800                     cls: 'separator'
21801                 },
21802                 {
21803                     tag: 'td',
21804                     cn: [
21805                         {
21806                             tag: 'button',
21807                             type: 'button',
21808                             cls: 'btn btn-primary period',
21809                             html: 'AM'
21810                             
21811                         }
21812                     ]
21813                 }
21814             ]
21815         });
21816         
21817         time.createChild({
21818             tag: 'tr',
21819             cn: [
21820                 {
21821                     tag: 'td',
21822                     cn: [
21823                         {
21824                             tag: 'a',
21825                             href: '#',
21826                             cls: 'btn',
21827                             cn: [
21828                                 {
21829                                     tag: 'span',
21830                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21831                                 }
21832                             ]
21833                         }
21834                     ]
21835                 },
21836                 {
21837                     tag: 'td',
21838                     cls: 'separator'
21839                 },
21840                 {
21841                     tag: 'td',
21842                     cn: [
21843                         {
21844                             tag: 'a',
21845                             href: '#',
21846                             cls: 'btn',
21847                             cn: [
21848                                 {
21849                                     tag: 'span',
21850                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21851                                 }
21852                             ]
21853                         }
21854                     ]
21855                 },
21856                 {
21857                     tag: 'td',
21858                     cls: 'separator'
21859                 }
21860             ]
21861         });
21862         
21863     },
21864     
21865     update: function()
21866     {
21867         
21868         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21869         
21870         this.fill();
21871     },
21872     
21873     fill: function() 
21874     {
21875         var hours = this.time.getHours();
21876         var minutes = this.time.getMinutes();
21877         var period = 'AM';
21878         
21879         if(hours > 11){
21880             period = 'PM';
21881         }
21882         
21883         if(hours == 0){
21884             hours = 12;
21885         }
21886         
21887         
21888         if(hours > 12){
21889             hours = hours - 12;
21890         }
21891         
21892         if(hours < 10){
21893             hours = '0' + hours;
21894         }
21895         
21896         if(minutes < 10){
21897             minutes = '0' + minutes;
21898         }
21899         
21900         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21901         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21902         this.pop.select('button', true).first().dom.innerHTML = period;
21903         
21904     },
21905     
21906     place: function()
21907     {   
21908         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21909         
21910         var cls = ['bottom'];
21911         
21912         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21913             cls.pop();
21914             cls.push('top');
21915         }
21916         
21917         cls.push('right');
21918         
21919         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21920             cls.pop();
21921             cls.push('left');
21922         }
21923         
21924         this.picker().addClass(cls.join('-'));
21925         
21926         var _this = this;
21927         
21928         Roo.each(cls, function(c){
21929             if(c == 'bottom'){
21930                 _this.picker().setTop(_this.inputEl().getHeight());
21931                 return;
21932             }
21933             if(c == 'top'){
21934                 _this.picker().setTop(0 - _this.picker().getHeight());
21935                 return;
21936             }
21937             
21938             if(c == 'left'){
21939                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21940                 return;
21941             }
21942             if(c == 'right'){
21943                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21944                 return;
21945             }
21946         });
21947         
21948     },
21949   
21950     onFocus : function()
21951     {
21952         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21953         this.show();
21954     },
21955     
21956     onBlur : function()
21957     {
21958         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21959         this.hide();
21960     },
21961     
21962     show : function()
21963     {
21964         this.picker().show();
21965         this.pop.show();
21966         this.update();
21967         this.place();
21968         
21969         this.fireEvent('show', this, this.date);
21970     },
21971     
21972     hide : function()
21973     {
21974         this.picker().hide();
21975         this.pop.hide();
21976         
21977         this.fireEvent('hide', this, this.date);
21978     },
21979     
21980     setTime : function()
21981     {
21982         this.hide();
21983         this.setValue(this.time.format(this.format));
21984         
21985         this.fireEvent('select', this, this.date);
21986         
21987         
21988     },
21989     
21990     onMousedown: function(e){
21991         e.stopPropagation();
21992         e.preventDefault();
21993     },
21994     
21995     onIncrementHours: function()
21996     {
21997         Roo.log('onIncrementHours');
21998         this.time = this.time.add(Date.HOUR, 1);
21999         this.update();
22000         
22001     },
22002     
22003     onDecrementHours: function()
22004     {
22005         Roo.log('onDecrementHours');
22006         this.time = this.time.add(Date.HOUR, -1);
22007         this.update();
22008     },
22009     
22010     onIncrementMinutes: function()
22011     {
22012         Roo.log('onIncrementMinutes');
22013         this.time = this.time.add(Date.MINUTE, 1);
22014         this.update();
22015     },
22016     
22017     onDecrementMinutes: function()
22018     {
22019         Roo.log('onDecrementMinutes');
22020         this.time = this.time.add(Date.MINUTE, -1);
22021         this.update();
22022     },
22023     
22024     onTogglePeriod: function()
22025     {
22026         Roo.log('onTogglePeriod');
22027         this.time = this.time.add(Date.HOUR, 12);
22028         this.update();
22029     }
22030     
22031    
22032 });
22033
22034 Roo.apply(Roo.bootstrap.TimeField,  {
22035     
22036     content : {
22037         tag: 'tbody',
22038         cn: [
22039             {
22040                 tag: 'tr',
22041                 cn: [
22042                 {
22043                     tag: 'td',
22044                     colspan: '7'
22045                 }
22046                 ]
22047             }
22048         ]
22049     },
22050     
22051     footer : {
22052         tag: 'tfoot',
22053         cn: [
22054             {
22055                 tag: 'tr',
22056                 cn: [
22057                 {
22058                     tag: 'th',
22059                     colspan: '7',
22060                     cls: '',
22061                     cn: [
22062                         {
22063                             tag: 'button',
22064                             cls: 'btn btn-info ok',
22065                             html: 'OK'
22066                         }
22067                     ]
22068                 }
22069
22070                 ]
22071             }
22072         ]
22073     }
22074 });
22075
22076 Roo.apply(Roo.bootstrap.TimeField,  {
22077   
22078     template : {
22079         tag: 'div',
22080         cls: 'datepicker dropdown-menu',
22081         cn: [
22082             {
22083                 tag: 'div',
22084                 cls: 'datepicker-time',
22085                 cn: [
22086                 {
22087                     tag: 'table',
22088                     cls: 'table-condensed',
22089                     cn:[
22090                     Roo.bootstrap.TimeField.content,
22091                     Roo.bootstrap.TimeField.footer
22092                     ]
22093                 }
22094                 ]
22095             }
22096         ]
22097     }
22098 });
22099
22100  
22101
22102  /*
22103  * - LGPL
22104  *
22105  * MonthField
22106  * 
22107  */
22108
22109 /**
22110  * @class Roo.bootstrap.MonthField
22111  * @extends Roo.bootstrap.Input
22112  * Bootstrap MonthField class
22113  * 
22114  * @cfg {String} language default en
22115  * 
22116  * @constructor
22117  * Create a new MonthField
22118  * @param {Object} config The config object
22119  */
22120
22121 Roo.bootstrap.MonthField = function(config){
22122     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22123     
22124     this.addEvents({
22125         /**
22126          * @event show
22127          * Fires when this field show.
22128          * @param {Roo.bootstrap.MonthField} this
22129          * @param {Mixed} date The date value
22130          */
22131         show : true,
22132         /**
22133          * @event show
22134          * Fires when this field hide.
22135          * @param {Roo.bootstrap.MonthField} this
22136          * @param {Mixed} date The date value
22137          */
22138         hide : true,
22139         /**
22140          * @event select
22141          * Fires when select a date.
22142          * @param {Roo.bootstrap.MonthField} this
22143          * @param {String} oldvalue The old value
22144          * @param {String} newvalue The new value
22145          */
22146         select : true
22147     });
22148 };
22149
22150 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22151     
22152     onRender: function(ct, position)
22153     {
22154         
22155         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22156         
22157         this.language = this.language || 'en';
22158         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22159         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22160         
22161         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22162         this.isInline = false;
22163         this.isInput = true;
22164         this.component = this.el.select('.add-on', true).first() || false;
22165         this.component = (this.component && this.component.length === 0) ? false : this.component;
22166         this.hasInput = this.component && this.inputEL().length;
22167         
22168         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22169         
22170         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22171         
22172         this.picker().on('mousedown', this.onMousedown, this);
22173         this.picker().on('click', this.onClick, this);
22174         
22175         this.picker().addClass('datepicker-dropdown');
22176         
22177         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22178             v.setStyle('width', '189px');
22179         });
22180         
22181         this.fillMonths();
22182         
22183         this.update();
22184         
22185         if(this.isInline) {
22186             this.show();
22187         }
22188         
22189     },
22190     
22191     setValue: function(v, suppressEvent)
22192     {   
22193         var o = this.getValue();
22194         
22195         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22196         
22197         this.update();
22198
22199         if(suppressEvent !== true){
22200             this.fireEvent('select', this, o, v);
22201         }
22202         
22203     },
22204     
22205     getValue: function()
22206     {
22207         return this.value;
22208     },
22209     
22210     onClick: function(e) 
22211     {
22212         e.stopPropagation();
22213         e.preventDefault();
22214         
22215         var target = e.getTarget();
22216         
22217         if(target.nodeName.toLowerCase() === 'i'){
22218             target = Roo.get(target).dom.parentNode;
22219         }
22220         
22221         var nodeName = target.nodeName;
22222         var className = target.className;
22223         var html = target.innerHTML;
22224         
22225         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22226             return;
22227         }
22228         
22229         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22230         
22231         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22232         
22233         this.hide();
22234                         
22235     },
22236     
22237     picker : function()
22238     {
22239         return this.pickerEl;
22240     },
22241     
22242     fillMonths: function()
22243     {    
22244         var i = 0;
22245         var months = this.picker().select('>.datepicker-months td', true).first();
22246         
22247         months.dom.innerHTML = '';
22248         
22249         while (i < 12) {
22250             var month = {
22251                 tag: 'span',
22252                 cls: 'month',
22253                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22254             };
22255             
22256             months.createChild(month);
22257         }
22258         
22259     },
22260     
22261     update: function()
22262     {
22263         var _this = this;
22264         
22265         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22266             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22267         }
22268         
22269         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22270             e.removeClass('active');
22271             
22272             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22273                 e.addClass('active');
22274             }
22275         })
22276     },
22277     
22278     place: function()
22279     {
22280         if(this.isInline) {
22281             return;
22282         }
22283         
22284         this.picker().removeClass(['bottom', 'top']);
22285         
22286         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22287             /*
22288              * place to the top of element!
22289              *
22290              */
22291             
22292             this.picker().addClass('top');
22293             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22294             
22295             return;
22296         }
22297         
22298         this.picker().addClass('bottom');
22299         
22300         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22301     },
22302     
22303     onFocus : function()
22304     {
22305         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22306         this.show();
22307     },
22308     
22309     onBlur : function()
22310     {
22311         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22312         
22313         var d = this.inputEl().getValue();
22314         
22315         this.setValue(d);
22316                 
22317         this.hide();
22318     },
22319     
22320     show : function()
22321     {
22322         this.picker().show();
22323         this.picker().select('>.datepicker-months', true).first().show();
22324         this.update();
22325         this.place();
22326         
22327         this.fireEvent('show', this, this.date);
22328     },
22329     
22330     hide : function()
22331     {
22332         if(this.isInline) {
22333             return;
22334         }
22335         this.picker().hide();
22336         this.fireEvent('hide', this, this.date);
22337         
22338     },
22339     
22340     onMousedown: function(e)
22341     {
22342         e.stopPropagation();
22343         e.preventDefault();
22344     },
22345     
22346     keyup: function(e)
22347     {
22348         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22349         this.update();
22350     },
22351
22352     fireKey: function(e)
22353     {
22354         if (!this.picker().isVisible()){
22355             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22356                 this.show();
22357             }
22358             return;
22359         }
22360         
22361         var dir;
22362         
22363         switch(e.keyCode){
22364             case 27: // escape
22365                 this.hide();
22366                 e.preventDefault();
22367                 break;
22368             case 37: // left
22369             case 39: // right
22370                 dir = e.keyCode == 37 ? -1 : 1;
22371                 
22372                 this.vIndex = this.vIndex + dir;
22373                 
22374                 if(this.vIndex < 0){
22375                     this.vIndex = 0;
22376                 }
22377                 
22378                 if(this.vIndex > 11){
22379                     this.vIndex = 11;
22380                 }
22381                 
22382                 if(isNaN(this.vIndex)){
22383                     this.vIndex = 0;
22384                 }
22385                 
22386                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22387                 
22388                 break;
22389             case 38: // up
22390             case 40: // down
22391                 
22392                 dir = e.keyCode == 38 ? -1 : 1;
22393                 
22394                 this.vIndex = this.vIndex + dir * 4;
22395                 
22396                 if(this.vIndex < 0){
22397                     this.vIndex = 0;
22398                 }
22399                 
22400                 if(this.vIndex > 11){
22401                     this.vIndex = 11;
22402                 }
22403                 
22404                 if(isNaN(this.vIndex)){
22405                     this.vIndex = 0;
22406                 }
22407                 
22408                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22409                 break;
22410                 
22411             case 13: // enter
22412                 
22413                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22414                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22415                 }
22416                 
22417                 this.hide();
22418                 e.preventDefault();
22419                 break;
22420             case 9: // tab
22421                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22422                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22423                 }
22424                 this.hide();
22425                 break;
22426             case 16: // shift
22427             case 17: // ctrl
22428             case 18: // alt
22429                 break;
22430             default :
22431                 this.hide();
22432                 
22433         }
22434     },
22435     
22436     remove: function() 
22437     {
22438         this.picker().remove();
22439     }
22440    
22441 });
22442
22443 Roo.apply(Roo.bootstrap.MonthField,  {
22444     
22445     content : {
22446         tag: 'tbody',
22447         cn: [
22448         {
22449             tag: 'tr',
22450             cn: [
22451             {
22452                 tag: 'td',
22453                 colspan: '7'
22454             }
22455             ]
22456         }
22457         ]
22458     },
22459     
22460     dates:{
22461         en: {
22462             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22463             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22464         }
22465     }
22466 });
22467
22468 Roo.apply(Roo.bootstrap.MonthField,  {
22469   
22470     template : {
22471         tag: 'div',
22472         cls: 'datepicker dropdown-menu roo-dynamic',
22473         cn: [
22474             {
22475                 tag: 'div',
22476                 cls: 'datepicker-months',
22477                 cn: [
22478                 {
22479                     tag: 'table',
22480                     cls: 'table-condensed',
22481                     cn:[
22482                         Roo.bootstrap.DateField.content
22483                     ]
22484                 }
22485                 ]
22486             }
22487         ]
22488     }
22489 });
22490
22491  
22492
22493  
22494  /*
22495  * - LGPL
22496  *
22497  * CheckBox
22498  * 
22499  */
22500
22501 /**
22502  * @class Roo.bootstrap.CheckBox
22503  * @extends Roo.bootstrap.Input
22504  * Bootstrap CheckBox class
22505  * 
22506  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22507  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22508  * @cfg {String} boxLabel The text that appears beside the checkbox
22509  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22510  * @cfg {Boolean} checked initnal the element
22511  * @cfg {Boolean} inline inline the element (default false)
22512  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22513  * @cfg {String} tooltip label tooltip
22514  * 
22515  * @constructor
22516  * Create a new CheckBox
22517  * @param {Object} config The config object
22518  */
22519
22520 Roo.bootstrap.CheckBox = function(config){
22521     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22522    
22523     this.addEvents({
22524         /**
22525         * @event check
22526         * Fires when the element is checked or unchecked.
22527         * @param {Roo.bootstrap.CheckBox} this This input
22528         * @param {Boolean} checked The new checked value
22529         */
22530        check : true,
22531        /**
22532         * @event click
22533         * Fires when the element is click.
22534         * @param {Roo.bootstrap.CheckBox} this This input
22535         */
22536        click : true
22537     });
22538     
22539 };
22540
22541 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22542   
22543     inputType: 'checkbox',
22544     inputValue: 1,
22545     valueOff: 0,
22546     boxLabel: false,
22547     checked: false,
22548     weight : false,
22549     inline: false,
22550     tooltip : '',
22551     
22552     // checkbox success does not make any sense really.. 
22553     invalidClass : "",
22554     validClass : "",
22555     
22556     
22557     getAutoCreate : function()
22558     {
22559         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22560         
22561         var id = Roo.id();
22562         
22563         var cfg = {};
22564         
22565         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22566         
22567         if(this.inline){
22568             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22569         }
22570         
22571         var input =  {
22572             tag: 'input',
22573             id : id,
22574             type : this.inputType,
22575             value : this.inputValue,
22576             cls : 'roo-' + this.inputType, //'form-box',
22577             placeholder : this.placeholder || ''
22578             
22579         };
22580         
22581         if(this.inputType != 'radio'){
22582             var hidden =  {
22583                 tag: 'input',
22584                 type : 'hidden',
22585                 cls : 'roo-hidden-value',
22586                 value : this.checked ? this.inputValue : this.valueOff
22587             };
22588         }
22589         
22590             
22591         if (this.weight) { // Validity check?
22592             cfg.cls += " " + this.inputType + "-" + this.weight;
22593         }
22594         
22595         if (this.disabled) {
22596             input.disabled=true;
22597         }
22598         
22599         if(this.checked){
22600             input.checked = this.checked;
22601         }
22602         
22603         if (this.name) {
22604             
22605             input.name = this.name;
22606             
22607             if(this.inputType != 'radio'){
22608                 hidden.name = this.name;
22609                 input.name = '_hidden_' + this.name;
22610             }
22611         }
22612         
22613         if (this.size) {
22614             input.cls += ' input-' + this.size;
22615         }
22616         
22617         var settings=this;
22618         
22619         ['xs','sm','md','lg'].map(function(size){
22620             if (settings[size]) {
22621                 cfg.cls += ' col-' + size + '-' + settings[size];
22622             }
22623         });
22624         
22625         var inputblock = input;
22626          
22627         if (this.before || this.after) {
22628             
22629             inputblock = {
22630                 cls : 'input-group',
22631                 cn :  [] 
22632             };
22633             
22634             if (this.before) {
22635                 inputblock.cn.push({
22636                     tag :'span',
22637                     cls : 'input-group-addon',
22638                     html : this.before
22639                 });
22640             }
22641             
22642             inputblock.cn.push(input);
22643             
22644             if(this.inputType != 'radio'){
22645                 inputblock.cn.push(hidden);
22646             }
22647             
22648             if (this.after) {
22649                 inputblock.cn.push({
22650                     tag :'span',
22651                     cls : 'input-group-addon',
22652                     html : this.after
22653                 });
22654             }
22655             
22656         }
22657         var boxLabelCfg = false;
22658         
22659         if(this.boxLabel){
22660            
22661             boxLabelCfg = {
22662                 tag: 'label',
22663                 //'for': id, // box label is handled by onclick - so no for...
22664                 cls: 'box-label',
22665                 html: this.boxLabel
22666             };
22667             if(this.tooltip){
22668                 boxLabelCfg.tooltip = this.tooltip;
22669             }
22670              
22671         }
22672         
22673         
22674         if (align ==='left' && this.fieldLabel.length) {
22675 //                Roo.log("left and has label");
22676             cfg.cn = [
22677                 {
22678                     tag: 'label',
22679                     'for' :  id,
22680                     cls : 'control-label',
22681                     html : this.fieldLabel
22682                 },
22683                 {
22684                     cls : "", 
22685                     cn: [
22686                         inputblock
22687                     ]
22688                 }
22689             ];
22690             
22691             if (boxLabelCfg) {
22692                 cfg.cn[1].cn.push(boxLabelCfg);
22693             }
22694             
22695             if(this.labelWidth > 12){
22696                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22697             }
22698             
22699             if(this.labelWidth < 13 && this.labelmd == 0){
22700                 this.labelmd = this.labelWidth;
22701             }
22702             
22703             if(this.labellg > 0){
22704                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22705                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22706             }
22707             
22708             if(this.labelmd > 0){
22709                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22710                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22711             }
22712             
22713             if(this.labelsm > 0){
22714                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22715                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22716             }
22717             
22718             if(this.labelxs > 0){
22719                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22720                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22721             }
22722             
22723         } else if ( this.fieldLabel.length) {
22724 //                Roo.log(" label");
22725                 cfg.cn = [
22726                    
22727                     {
22728                         tag: this.boxLabel ? 'span' : 'label',
22729                         'for': id,
22730                         cls: 'control-label box-input-label',
22731                         //cls : 'input-group-addon',
22732                         html : this.fieldLabel
22733                     },
22734                     
22735                     inputblock
22736                     
22737                 ];
22738                 if (boxLabelCfg) {
22739                     cfg.cn.push(boxLabelCfg);
22740                 }
22741
22742         } else {
22743             
22744 //                Roo.log(" no label && no align");
22745                 cfg.cn = [  inputblock ] ;
22746                 if (boxLabelCfg) {
22747                     cfg.cn.push(boxLabelCfg);
22748                 }
22749
22750                 
22751         }
22752         
22753        
22754         
22755         if(this.inputType != 'radio'){
22756             cfg.cn.push(hidden);
22757         }
22758         
22759         return cfg;
22760         
22761     },
22762     
22763     /**
22764      * return the real input element.
22765      */
22766     inputEl: function ()
22767     {
22768         return this.el.select('input.roo-' + this.inputType,true).first();
22769     },
22770     hiddenEl: function ()
22771     {
22772         return this.el.select('input.roo-hidden-value',true).first();
22773     },
22774     
22775     labelEl: function()
22776     {
22777         return this.el.select('label.control-label',true).first();
22778     },
22779     /* depricated... */
22780     
22781     label: function()
22782     {
22783         return this.labelEl();
22784     },
22785     
22786     boxLabelEl: function()
22787     {
22788         return this.el.select('label.box-label',true).first();
22789     },
22790     
22791     initEvents : function()
22792     {
22793 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22794         
22795         this.inputEl().on('click', this.onClick,  this);
22796         
22797         if (this.boxLabel) { 
22798             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22799         }
22800         
22801         this.startValue = this.getValue();
22802         
22803         if(this.groupId){
22804             Roo.bootstrap.CheckBox.register(this);
22805         }
22806     },
22807     
22808     onClick : function(e)
22809     {   
22810         if(this.fireEvent('click', this, e) !== false){
22811             this.setChecked(!this.checked);
22812         }
22813         
22814     },
22815     
22816     setChecked : function(state,suppressEvent)
22817     {
22818         this.startValue = this.getValue();
22819
22820         if(this.inputType == 'radio'){
22821             
22822             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22823                 e.dom.checked = false;
22824             });
22825             
22826             this.inputEl().dom.checked = true;
22827             
22828             this.inputEl().dom.value = this.inputValue;
22829             
22830             if(suppressEvent !== true){
22831                 this.fireEvent('check', this, true);
22832             }
22833             
22834             this.validate();
22835             
22836             return;
22837         }
22838         
22839         this.checked = state;
22840         
22841         this.inputEl().dom.checked = state;
22842         
22843         
22844         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22845         
22846         if(suppressEvent !== true){
22847             this.fireEvent('check', this, state);
22848         }
22849         
22850         this.validate();
22851     },
22852     
22853     getValue : function()
22854     {
22855         if(this.inputType == 'radio'){
22856             return this.getGroupValue();
22857         }
22858         
22859         return this.hiddenEl().dom.value;
22860         
22861     },
22862     
22863     getGroupValue : function()
22864     {
22865         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22866             return '';
22867         }
22868         
22869         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22870     },
22871     
22872     setValue : function(v,suppressEvent)
22873     {
22874         if(this.inputType == 'radio'){
22875             this.setGroupValue(v, suppressEvent);
22876             return;
22877         }
22878         
22879         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22880         
22881         this.validate();
22882     },
22883     
22884     setGroupValue : function(v, suppressEvent)
22885     {
22886         this.startValue = this.getValue();
22887         
22888         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22889             e.dom.checked = false;
22890             
22891             if(e.dom.value == v){
22892                 e.dom.checked = true;
22893             }
22894         });
22895         
22896         if(suppressEvent !== true){
22897             this.fireEvent('check', this, true);
22898         }
22899
22900         this.validate();
22901         
22902         return;
22903     },
22904     
22905     validate : function()
22906     {
22907         if(this.getVisibilityEl().hasClass('hidden')){
22908             return true;
22909         }
22910         
22911         if(
22912                 this.disabled || 
22913                 (this.inputType == 'radio' && this.validateRadio()) ||
22914                 (this.inputType == 'checkbox' && this.validateCheckbox())
22915         ){
22916             this.markValid();
22917             return true;
22918         }
22919         
22920         this.markInvalid();
22921         return false;
22922     },
22923     
22924     validateRadio : function()
22925     {
22926         if(this.getVisibilityEl().hasClass('hidden')){
22927             return true;
22928         }
22929         
22930         if(this.allowBlank){
22931             return true;
22932         }
22933         
22934         var valid = false;
22935         
22936         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22937             if(!e.dom.checked){
22938                 return;
22939             }
22940             
22941             valid = true;
22942             
22943             return false;
22944         });
22945         
22946         return valid;
22947     },
22948     
22949     validateCheckbox : function()
22950     {
22951         if(!this.groupId){
22952             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22953             //return (this.getValue() == this.inputValue) ? true : false;
22954         }
22955         
22956         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22957         
22958         if(!group){
22959             return false;
22960         }
22961         
22962         var r = false;
22963         
22964         for(var i in group){
22965             if(group[i].el.isVisible(true)){
22966                 r = false;
22967                 break;
22968             }
22969             
22970             r = true;
22971         }
22972         
22973         for(var i in group){
22974             if(r){
22975                 break;
22976             }
22977             
22978             r = (group[i].getValue() == group[i].inputValue) ? true : false;
22979         }
22980         
22981         return r;
22982     },
22983     
22984     /**
22985      * Mark this field as valid
22986      */
22987     markValid : function()
22988     {
22989         var _this = this;
22990         
22991         this.fireEvent('valid', this);
22992         
22993         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22994         
22995         if(this.groupId){
22996             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22997         }
22998         
22999         if(label){
23000             label.markValid();
23001         }
23002
23003         if(this.inputType == 'radio'){
23004             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23005                 var fg = e.findParent('.form-group', false, true);
23006                 if (Roo.bootstrap.version == 3) {
23007                     fg.removeClass([_this.invalidClass, _this.validClass]);
23008                     fg.addClass(_this.validClass);
23009                 } else {
23010                     fg.removeClass(['is-valid', 'is-invalid']);
23011                     fg.addClass('is-valid');
23012                 }
23013             });
23014             
23015             return;
23016         }
23017
23018         if(!this.groupId){
23019             var fg = this.el.findParent('.form-group', false, true);
23020             if (Roo.bootstrap.version == 3) {
23021                 fg.removeClass([this.invalidClass, this.validClass]);
23022                 fg.addClass(this.validClass);
23023             } else {
23024                 fg.removeClass(['is-valid', 'is-invalid']);
23025                 fg.addClass('is-valid');
23026             }
23027             return;
23028         }
23029         
23030         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23031         
23032         if(!group){
23033             return;
23034         }
23035         
23036         for(var i in group){
23037             var fg = group[i].el.findParent('.form-group', false, true);
23038             if (Roo.bootstrap.version == 3) {
23039                 fg.removeClass([this.invalidClass, this.validClass]);
23040                 fg.addClass(this.validClass);
23041             } else {
23042                 fg.removeClass(['is-valid', 'is-invalid']);
23043                 fg.addClass('is-valid');
23044             }
23045         }
23046     },
23047     
23048      /**
23049      * Mark this field as invalid
23050      * @param {String} msg The validation message
23051      */
23052     markInvalid : function(msg)
23053     {
23054         if(this.allowBlank){
23055             return;
23056         }
23057         
23058         var _this = this;
23059         
23060         this.fireEvent('invalid', this, msg);
23061         
23062         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23063         
23064         if(this.groupId){
23065             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23066         }
23067         
23068         if(label){
23069             label.markInvalid();
23070         }
23071             
23072         if(this.inputType == 'radio'){
23073             
23074             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23075                 var fg = e.findParent('.form-group', false, true);
23076                 if (Roo.bootstrap.version == 3) {
23077                     fg.removeClass([_this.invalidClass, _this.validClass]);
23078                     fg.addClass(_this.invalidClass);
23079                 } else {
23080                     fg.removeClass(['is-invalid', 'is-valid']);
23081                     fg.addClass('is-invalid');
23082                 }
23083             });
23084             
23085             return;
23086         }
23087         
23088         if(!this.groupId){
23089             var fg = this.el.findParent('.form-group', false, true);
23090             if (Roo.bootstrap.version == 3) {
23091                 fg.removeClass([_this.invalidClass, _this.validClass]);
23092                 fg.addClass(_this.invalidClass);
23093             } else {
23094                 fg.removeClass(['is-invalid', 'is-valid']);
23095                 fg.addClass('is-invalid');
23096             }
23097             return;
23098         }
23099         
23100         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23101         
23102         if(!group){
23103             return;
23104         }
23105         
23106         for(var i in group){
23107             var fg = group[i].el.findParent('.form-group', false, true);
23108             if (Roo.bootstrap.version == 3) {
23109                 fg.removeClass([_this.invalidClass, _this.validClass]);
23110                 fg.addClass(_this.invalidClass);
23111             } else {
23112                 fg.removeClass(['is-invalid', 'is-valid']);
23113                 fg.addClass('is-invalid');
23114             }
23115         }
23116         
23117     },
23118     
23119     clearInvalid : function()
23120     {
23121         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23122         
23123         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23124         
23125         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23126         
23127         if (label && label.iconEl) {
23128             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23129             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23130         }
23131     },
23132     
23133     disable : function()
23134     {
23135         if(this.inputType != 'radio'){
23136             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23137             return;
23138         }
23139         
23140         var _this = this;
23141         
23142         if(this.rendered){
23143             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23144                 _this.getActionEl().addClass(this.disabledClass);
23145                 e.dom.disabled = true;
23146             });
23147         }
23148         
23149         this.disabled = true;
23150         this.fireEvent("disable", this);
23151         return this;
23152     },
23153
23154     enable : function()
23155     {
23156         if(this.inputType != 'radio'){
23157             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23158             return;
23159         }
23160         
23161         var _this = this;
23162         
23163         if(this.rendered){
23164             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23165                 _this.getActionEl().removeClass(this.disabledClass);
23166                 e.dom.disabled = false;
23167             });
23168         }
23169         
23170         this.disabled = false;
23171         this.fireEvent("enable", this);
23172         return this;
23173     },
23174     
23175     setBoxLabel : function(v)
23176     {
23177         this.boxLabel = v;
23178         
23179         if(this.rendered){
23180             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23181         }
23182     }
23183
23184 });
23185
23186 Roo.apply(Roo.bootstrap.CheckBox, {
23187     
23188     groups: {},
23189     
23190      /**
23191     * register a CheckBox Group
23192     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23193     */
23194     register : function(checkbox)
23195     {
23196         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23197             this.groups[checkbox.groupId] = {};
23198         }
23199         
23200         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23201             return;
23202         }
23203         
23204         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23205         
23206     },
23207     /**
23208     * fetch a CheckBox Group based on the group ID
23209     * @param {string} the group ID
23210     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23211     */
23212     get: function(groupId) {
23213         if (typeof(this.groups[groupId]) == 'undefined') {
23214             return false;
23215         }
23216         
23217         return this.groups[groupId] ;
23218     }
23219     
23220     
23221 });
23222 /*
23223  * - LGPL
23224  *
23225  * RadioItem
23226  * 
23227  */
23228
23229 /**
23230  * @class Roo.bootstrap.Radio
23231  * @extends Roo.bootstrap.Component
23232  * Bootstrap Radio class
23233  * @cfg {String} boxLabel - the label associated
23234  * @cfg {String} value - the value of radio
23235  * 
23236  * @constructor
23237  * Create a new Radio
23238  * @param {Object} config The config object
23239  */
23240 Roo.bootstrap.Radio = function(config){
23241     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23242     
23243 };
23244
23245 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23246     
23247     boxLabel : '',
23248     
23249     value : '',
23250     
23251     getAutoCreate : function()
23252     {
23253         var cfg = {
23254             tag : 'div',
23255             cls : 'form-group radio',
23256             cn : [
23257                 {
23258                     tag : 'label',
23259                     cls : 'box-label',
23260                     html : this.boxLabel
23261                 }
23262             ]
23263         };
23264         
23265         return cfg;
23266     },
23267     
23268     initEvents : function() 
23269     {
23270         this.parent().register(this);
23271         
23272         this.el.on('click', this.onClick, this);
23273         
23274     },
23275     
23276     onClick : function(e)
23277     {
23278         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23279             this.setChecked(true);
23280         }
23281     },
23282     
23283     setChecked : function(state, suppressEvent)
23284     {
23285         this.parent().setValue(this.value, suppressEvent);
23286         
23287     },
23288     
23289     setBoxLabel : function(v)
23290     {
23291         this.boxLabel = v;
23292         
23293         if(this.rendered){
23294             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23295         }
23296     }
23297     
23298 });
23299  
23300
23301  /*
23302  * - LGPL
23303  *
23304  * Input
23305  * 
23306  */
23307
23308 /**
23309  * @class Roo.bootstrap.SecurePass
23310  * @extends Roo.bootstrap.Input
23311  * Bootstrap SecurePass class
23312  *
23313  * 
23314  * @constructor
23315  * Create a new SecurePass
23316  * @param {Object} config The config object
23317  */
23318  
23319 Roo.bootstrap.SecurePass = function (config) {
23320     // these go here, so the translation tool can replace them..
23321     this.errors = {
23322         PwdEmpty: "Please type a password, and then retype it to confirm.",
23323         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23324         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23325         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23326         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23327         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23328         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23329         TooWeak: "Your password is Too Weak."
23330     },
23331     this.meterLabel = "Password strength:";
23332     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23333     this.meterClass = [
23334         "roo-password-meter-tooweak", 
23335         "roo-password-meter-weak", 
23336         "roo-password-meter-medium", 
23337         "roo-password-meter-strong", 
23338         "roo-password-meter-grey"
23339     ];
23340     
23341     this.errors = {};
23342     
23343     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23344 }
23345
23346 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23347     /**
23348      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23349      * {
23350      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23351      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23352      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23353      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23354      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23355      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23356      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23357      * })
23358      */
23359     // private
23360     
23361     meterWidth: 300,
23362     errorMsg :'',    
23363     errors: false,
23364     imageRoot: '/',
23365     /**
23366      * @cfg {String/Object} Label for the strength meter (defaults to
23367      * 'Password strength:')
23368      */
23369     // private
23370     meterLabel: '',
23371     /**
23372      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23373      * ['Weak', 'Medium', 'Strong'])
23374      */
23375     // private    
23376     pwdStrengths: false,    
23377     // private
23378     strength: 0,
23379     // private
23380     _lastPwd: null,
23381     // private
23382     kCapitalLetter: 0,
23383     kSmallLetter: 1,
23384     kDigit: 2,
23385     kPunctuation: 3,
23386     
23387     insecure: false,
23388     // private
23389     initEvents: function ()
23390     {
23391         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23392
23393         if (this.el.is('input[type=password]') && Roo.isSafari) {
23394             this.el.on('keydown', this.SafariOnKeyDown, this);
23395         }
23396
23397         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23398     },
23399     // private
23400     onRender: function (ct, position)
23401     {
23402         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23403         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23404         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23405
23406         this.trigger.createChild({
23407                    cn: [
23408                     {
23409                     //id: 'PwdMeter',
23410                     tag: 'div',
23411                     cls: 'roo-password-meter-grey col-xs-12',
23412                     style: {
23413                         //width: 0,
23414                         //width: this.meterWidth + 'px'                                                
23415                         }
23416                     },
23417                     {                            
23418                          cls: 'roo-password-meter-text'                          
23419                     }
23420                 ]            
23421         });
23422
23423          
23424         if (this.hideTrigger) {
23425             this.trigger.setDisplayed(false);
23426         }
23427         this.setSize(this.width || '', this.height || '');
23428     },
23429     // private
23430     onDestroy: function ()
23431     {
23432         if (this.trigger) {
23433             this.trigger.removeAllListeners();
23434             this.trigger.remove();
23435         }
23436         if (this.wrap) {
23437             this.wrap.remove();
23438         }
23439         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23440     },
23441     // private
23442     checkStrength: function ()
23443     {
23444         var pwd = this.inputEl().getValue();
23445         if (pwd == this._lastPwd) {
23446             return;
23447         }
23448
23449         var strength;
23450         if (this.ClientSideStrongPassword(pwd)) {
23451             strength = 3;
23452         } else if (this.ClientSideMediumPassword(pwd)) {
23453             strength = 2;
23454         } else if (this.ClientSideWeakPassword(pwd)) {
23455             strength = 1;
23456         } else {
23457             strength = 0;
23458         }
23459         
23460         Roo.log('strength1: ' + strength);
23461         
23462         //var pm = this.trigger.child('div/div/div').dom;
23463         var pm = this.trigger.child('div/div');
23464         pm.removeClass(this.meterClass);
23465         pm.addClass(this.meterClass[strength]);
23466                 
23467         
23468         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23469                 
23470         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23471         
23472         this._lastPwd = pwd;
23473     },
23474     reset: function ()
23475     {
23476         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23477         
23478         this._lastPwd = '';
23479         
23480         var pm = this.trigger.child('div/div');
23481         pm.removeClass(this.meterClass);
23482         pm.addClass('roo-password-meter-grey');        
23483         
23484         
23485         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23486         
23487         pt.innerHTML = '';
23488         this.inputEl().dom.type='password';
23489     },
23490     // private
23491     validateValue: function (value)
23492     {
23493         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23494             return false;
23495         }
23496         if (value.length == 0) {
23497             if (this.allowBlank) {
23498                 this.clearInvalid();
23499                 return true;
23500             }
23501
23502             this.markInvalid(this.errors.PwdEmpty);
23503             this.errorMsg = this.errors.PwdEmpty;
23504             return false;
23505         }
23506         
23507         if(this.insecure){
23508             return true;
23509         }
23510         
23511         if (!value.match(/[\x21-\x7e]+/)) {
23512             this.markInvalid(this.errors.PwdBadChar);
23513             this.errorMsg = this.errors.PwdBadChar;
23514             return false;
23515         }
23516         if (value.length < 6) {
23517             this.markInvalid(this.errors.PwdShort);
23518             this.errorMsg = this.errors.PwdShort;
23519             return false;
23520         }
23521         if (value.length > 16) {
23522             this.markInvalid(this.errors.PwdLong);
23523             this.errorMsg = this.errors.PwdLong;
23524             return false;
23525         }
23526         var strength;
23527         if (this.ClientSideStrongPassword(value)) {
23528             strength = 3;
23529         } else if (this.ClientSideMediumPassword(value)) {
23530             strength = 2;
23531         } else if (this.ClientSideWeakPassword(value)) {
23532             strength = 1;
23533         } else {
23534             strength = 0;
23535         }
23536
23537         
23538         if (strength < 2) {
23539             //this.markInvalid(this.errors.TooWeak);
23540             this.errorMsg = this.errors.TooWeak;
23541             //return false;
23542         }
23543         
23544         
23545         console.log('strength2: ' + strength);
23546         
23547         //var pm = this.trigger.child('div/div/div').dom;
23548         
23549         var pm = this.trigger.child('div/div');
23550         pm.removeClass(this.meterClass);
23551         pm.addClass(this.meterClass[strength]);
23552                 
23553         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23554                 
23555         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23556         
23557         this.errorMsg = ''; 
23558         return true;
23559     },
23560     // private
23561     CharacterSetChecks: function (type)
23562     {
23563         this.type = type;
23564         this.fResult = false;
23565     },
23566     // private
23567     isctype: function (character, type)
23568     {
23569         switch (type) {  
23570             case this.kCapitalLetter:
23571                 if (character >= 'A' && character <= 'Z') {
23572                     return true;
23573                 }
23574                 break;
23575             
23576             case this.kSmallLetter:
23577                 if (character >= 'a' && character <= 'z') {
23578                     return true;
23579                 }
23580                 break;
23581             
23582             case this.kDigit:
23583                 if (character >= '0' && character <= '9') {
23584                     return true;
23585                 }
23586                 break;
23587             
23588             case this.kPunctuation:
23589                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23590                     return true;
23591                 }
23592                 break;
23593             
23594             default:
23595                 return false;
23596         }
23597
23598     },
23599     // private
23600     IsLongEnough: function (pwd, size)
23601     {
23602         return !(pwd == null || isNaN(size) || pwd.length < size);
23603     },
23604     // private
23605     SpansEnoughCharacterSets: function (word, nb)
23606     {
23607         if (!this.IsLongEnough(word, nb))
23608         {
23609             return false;
23610         }
23611
23612         var characterSetChecks = new Array(
23613             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23614             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23615         );
23616         
23617         for (var index = 0; index < word.length; ++index) {
23618             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23619                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23620                     characterSetChecks[nCharSet].fResult = true;
23621                     break;
23622                 }
23623             }
23624         }
23625
23626         var nCharSets = 0;
23627         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23628             if (characterSetChecks[nCharSet].fResult) {
23629                 ++nCharSets;
23630             }
23631         }
23632
23633         if (nCharSets < nb) {
23634             return false;
23635         }
23636         return true;
23637     },
23638     // private
23639     ClientSideStrongPassword: function (pwd)
23640     {
23641         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23642     },
23643     // private
23644     ClientSideMediumPassword: function (pwd)
23645     {
23646         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23647     },
23648     // private
23649     ClientSideWeakPassword: function (pwd)
23650     {
23651         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23652     }
23653           
23654 })//<script type="text/javascript">
23655
23656 /*
23657  * Based  Ext JS Library 1.1.1
23658  * Copyright(c) 2006-2007, Ext JS, LLC.
23659  * LGPL
23660  *
23661  */
23662  
23663 /**
23664  * @class Roo.HtmlEditorCore
23665  * @extends Roo.Component
23666  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23667  *
23668  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23669  */
23670
23671 Roo.HtmlEditorCore = function(config){
23672     
23673     
23674     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23675     
23676     
23677     this.addEvents({
23678         /**
23679          * @event initialize
23680          * Fires when the editor is fully initialized (including the iframe)
23681          * @param {Roo.HtmlEditorCore} this
23682          */
23683         initialize: true,
23684         /**
23685          * @event activate
23686          * Fires when the editor is first receives the focus. Any insertion must wait
23687          * until after this event.
23688          * @param {Roo.HtmlEditorCore} this
23689          */
23690         activate: true,
23691          /**
23692          * @event beforesync
23693          * Fires before the textarea is updated with content from the editor iframe. Return false
23694          * to cancel the sync.
23695          * @param {Roo.HtmlEditorCore} this
23696          * @param {String} html
23697          */
23698         beforesync: true,
23699          /**
23700          * @event beforepush
23701          * Fires before the iframe editor is updated with content from the textarea. Return false
23702          * to cancel the push.
23703          * @param {Roo.HtmlEditorCore} this
23704          * @param {String} html
23705          */
23706         beforepush: true,
23707          /**
23708          * @event sync
23709          * Fires when the textarea is updated with content from the editor iframe.
23710          * @param {Roo.HtmlEditorCore} this
23711          * @param {String} html
23712          */
23713         sync: true,
23714          /**
23715          * @event push
23716          * Fires when the iframe editor is updated with content from the textarea.
23717          * @param {Roo.HtmlEditorCore} this
23718          * @param {String} html
23719          */
23720         push: true,
23721         
23722         /**
23723          * @event editorevent
23724          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23725          * @param {Roo.HtmlEditorCore} this
23726          */
23727         editorevent: true
23728         
23729     });
23730     
23731     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23732     
23733     // defaults : white / black...
23734     this.applyBlacklists();
23735     
23736     
23737     
23738 };
23739
23740
23741 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23742
23743
23744      /**
23745      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23746      */
23747     
23748     owner : false,
23749     
23750      /**
23751      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23752      *                        Roo.resizable.
23753      */
23754     resizable : false,
23755      /**
23756      * @cfg {Number} height (in pixels)
23757      */   
23758     height: 300,
23759    /**
23760      * @cfg {Number} width (in pixels)
23761      */   
23762     width: 500,
23763     
23764     /**
23765      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23766      * 
23767      */
23768     stylesheets: false,
23769     
23770     // id of frame..
23771     frameId: false,
23772     
23773     // private properties
23774     validationEvent : false,
23775     deferHeight: true,
23776     initialized : false,
23777     activated : false,
23778     sourceEditMode : false,
23779     onFocus : Roo.emptyFn,
23780     iframePad:3,
23781     hideMode:'offsets',
23782     
23783     clearUp: true,
23784     
23785     // blacklist + whitelisted elements..
23786     black: false,
23787     white: false,
23788      
23789     bodyCls : '',
23790
23791     /**
23792      * Protected method that will not generally be called directly. It
23793      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23794      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23795      */
23796     getDocMarkup : function(){
23797         // body styles..
23798         var st = '';
23799         
23800         // inherit styels from page...?? 
23801         if (this.stylesheets === false) {
23802             
23803             Roo.get(document.head).select('style').each(function(node) {
23804                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23805             });
23806             
23807             Roo.get(document.head).select('link').each(function(node) { 
23808                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23809             });
23810             
23811         } else if (!this.stylesheets.length) {
23812                 // simple..
23813                 st = '<style type="text/css">' +
23814                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23815                    '</style>';
23816         } else {
23817             for (var i in this.stylesheets) { 
23818                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23819             }
23820             
23821         }
23822         
23823         st +=  '<style type="text/css">' +
23824             'IMG { cursor: pointer } ' +
23825         '</style>';
23826
23827         var cls = 'roo-htmleditor-body';
23828         
23829         if(this.bodyCls.length){
23830             cls += ' ' + this.bodyCls;
23831         }
23832         
23833         return '<html><head>' + st  +
23834             //<style type="text/css">' +
23835             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23836             //'</style>' +
23837             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23838     },
23839
23840     // private
23841     onRender : function(ct, position)
23842     {
23843         var _t = this;
23844         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23845         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23846         
23847         
23848         this.el.dom.style.border = '0 none';
23849         this.el.dom.setAttribute('tabIndex', -1);
23850         this.el.addClass('x-hidden hide');
23851         
23852         
23853         
23854         if(Roo.isIE){ // fix IE 1px bogus margin
23855             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23856         }
23857        
23858         
23859         this.frameId = Roo.id();
23860         
23861          
23862         
23863         var iframe = this.owner.wrap.createChild({
23864             tag: 'iframe',
23865             cls: 'form-control', // bootstrap..
23866             id: this.frameId,
23867             name: this.frameId,
23868             frameBorder : 'no',
23869             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23870         }, this.el
23871         );
23872         
23873         
23874         this.iframe = iframe.dom;
23875
23876          this.assignDocWin();
23877         
23878         this.doc.designMode = 'on';
23879        
23880         this.doc.open();
23881         this.doc.write(this.getDocMarkup());
23882         this.doc.close();
23883
23884         
23885         var task = { // must defer to wait for browser to be ready
23886             run : function(){
23887                 //console.log("run task?" + this.doc.readyState);
23888                 this.assignDocWin();
23889                 if(this.doc.body || this.doc.readyState == 'complete'){
23890                     try {
23891                         this.doc.designMode="on";
23892                     } catch (e) {
23893                         return;
23894                     }
23895                     Roo.TaskMgr.stop(task);
23896                     this.initEditor.defer(10, this);
23897                 }
23898             },
23899             interval : 10,
23900             duration: 10000,
23901             scope: this
23902         };
23903         Roo.TaskMgr.start(task);
23904
23905     },
23906
23907     // private
23908     onResize : function(w, h)
23909     {
23910          Roo.log('resize: ' +w + ',' + h );
23911         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23912         if(!this.iframe){
23913             return;
23914         }
23915         if(typeof w == 'number'){
23916             
23917             this.iframe.style.width = w + 'px';
23918         }
23919         if(typeof h == 'number'){
23920             
23921             this.iframe.style.height = h + 'px';
23922             if(this.doc){
23923                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23924             }
23925         }
23926         
23927     },
23928
23929     /**
23930      * Toggles the editor between standard and source edit mode.
23931      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23932      */
23933     toggleSourceEdit : function(sourceEditMode){
23934         
23935         this.sourceEditMode = sourceEditMode === true;
23936         
23937         if(this.sourceEditMode){
23938  
23939             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
23940             
23941         }else{
23942             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23943             //this.iframe.className = '';
23944             this.deferFocus();
23945         }
23946         //this.setSize(this.owner.wrap.getSize());
23947         //this.fireEvent('editmodechange', this, this.sourceEditMode);
23948     },
23949
23950     
23951   
23952
23953     /**
23954      * Protected method that will not generally be called directly. If you need/want
23955      * custom HTML cleanup, this is the method you should override.
23956      * @param {String} html The HTML to be cleaned
23957      * return {String} The cleaned HTML
23958      */
23959     cleanHtml : function(html){
23960         html = String(html);
23961         if(html.length > 5){
23962             if(Roo.isSafari){ // strip safari nonsense
23963                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23964             }
23965         }
23966         if(html == '&nbsp;'){
23967             html = '';
23968         }
23969         return html;
23970     },
23971
23972     /**
23973      * HTML Editor -> Textarea
23974      * Protected method that will not generally be called directly. Syncs the contents
23975      * of the editor iframe with the textarea.
23976      */
23977     syncValue : function(){
23978         if(this.initialized){
23979             var bd = (this.doc.body || this.doc.documentElement);
23980             //this.cleanUpPaste(); -- this is done else where and causes havoc..
23981             var html = bd.innerHTML;
23982             if(Roo.isSafari){
23983                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23984                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23985                 if(m && m[1]){
23986                     html = '<div style="'+m[0]+'">' + html + '</div>';
23987                 }
23988             }
23989             html = this.cleanHtml(html);
23990             // fix up the special chars.. normaly like back quotes in word...
23991             // however we do not want to do this with chinese..
23992             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23993                 
23994                 var cc = match.charCodeAt();
23995
23996                 // Get the character value, handling surrogate pairs
23997                 if (match.length == 2) {
23998                     // It's a surrogate pair, calculate the Unicode code point
23999                     var high = match.charCodeAt(0) - 0xD800;
24000                     var low  = match.charCodeAt(1) - 0xDC00;
24001                     cc = (high * 0x400) + low + 0x10000;
24002                 }  else if (
24003                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24004                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24005                     (cc >= 0xf900 && cc < 0xfb00 )
24006                 ) {
24007                         return match;
24008                 }  
24009          
24010                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24011                 return "&#" + cc + ";";
24012                 
24013                 
24014             });
24015             
24016             
24017              
24018             if(this.owner.fireEvent('beforesync', this, html) !== false){
24019                 this.el.dom.value = html;
24020                 this.owner.fireEvent('sync', this, html);
24021             }
24022         }
24023     },
24024
24025     /**
24026      * Protected method that will not generally be called directly. Pushes the value of the textarea
24027      * into the iframe editor.
24028      */
24029     pushValue : function(){
24030         if(this.initialized){
24031             var v = this.el.dom.value.trim();
24032             
24033 //            if(v.length < 1){
24034 //                v = '&#160;';
24035 //            }
24036             
24037             if(this.owner.fireEvent('beforepush', this, v) !== false){
24038                 var d = (this.doc.body || this.doc.documentElement);
24039                 d.innerHTML = v;
24040                 this.cleanUpPaste();
24041                 this.el.dom.value = d.innerHTML;
24042                 this.owner.fireEvent('push', this, v);
24043             }
24044         }
24045     },
24046
24047     // private
24048     deferFocus : function(){
24049         this.focus.defer(10, this);
24050     },
24051
24052     // doc'ed in Field
24053     focus : function(){
24054         if(this.win && !this.sourceEditMode){
24055             this.win.focus();
24056         }else{
24057             this.el.focus();
24058         }
24059     },
24060     
24061     assignDocWin: function()
24062     {
24063         var iframe = this.iframe;
24064         
24065          if(Roo.isIE){
24066             this.doc = iframe.contentWindow.document;
24067             this.win = iframe.contentWindow;
24068         } else {
24069 //            if (!Roo.get(this.frameId)) {
24070 //                return;
24071 //            }
24072 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24073 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24074             
24075             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24076                 return;
24077             }
24078             
24079             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24080             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24081         }
24082     },
24083     
24084     // private
24085     initEditor : function(){
24086         //console.log("INIT EDITOR");
24087         this.assignDocWin();
24088         
24089         
24090         
24091         this.doc.designMode="on";
24092         this.doc.open();
24093         this.doc.write(this.getDocMarkup());
24094         this.doc.close();
24095         
24096         var dbody = (this.doc.body || this.doc.documentElement);
24097         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24098         // this copies styles from the containing element into thsi one..
24099         // not sure why we need all of this..
24100         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24101         
24102         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24103         //ss['background-attachment'] = 'fixed'; // w3c
24104         dbody.bgProperties = 'fixed'; // ie
24105         //Roo.DomHelper.applyStyles(dbody, ss);
24106         Roo.EventManager.on(this.doc, {
24107             //'mousedown': this.onEditorEvent,
24108             'mouseup': this.onEditorEvent,
24109             'dblclick': this.onEditorEvent,
24110             'click': this.onEditorEvent,
24111             'keyup': this.onEditorEvent,
24112             buffer:100,
24113             scope: this
24114         });
24115         if(Roo.isGecko){
24116             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24117         }
24118         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24119             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24120         }
24121         this.initialized = true;
24122
24123         this.owner.fireEvent('initialize', this);
24124         this.pushValue();
24125     },
24126
24127     // private
24128     onDestroy : function(){
24129         
24130         
24131         
24132         if(this.rendered){
24133             
24134             //for (var i =0; i < this.toolbars.length;i++) {
24135             //    // fixme - ask toolbars for heights?
24136             //    this.toolbars[i].onDestroy();
24137            // }
24138             
24139             //this.wrap.dom.innerHTML = '';
24140             //this.wrap.remove();
24141         }
24142     },
24143
24144     // private
24145     onFirstFocus : function(){
24146         
24147         this.assignDocWin();
24148         
24149         
24150         this.activated = true;
24151          
24152     
24153         if(Roo.isGecko){ // prevent silly gecko errors
24154             this.win.focus();
24155             var s = this.win.getSelection();
24156             if(!s.focusNode || s.focusNode.nodeType != 3){
24157                 var r = s.getRangeAt(0);
24158                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24159                 r.collapse(true);
24160                 this.deferFocus();
24161             }
24162             try{
24163                 this.execCmd('useCSS', true);
24164                 this.execCmd('styleWithCSS', false);
24165             }catch(e){}
24166         }
24167         this.owner.fireEvent('activate', this);
24168     },
24169
24170     // private
24171     adjustFont: function(btn){
24172         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24173         //if(Roo.isSafari){ // safari
24174         //    adjust *= 2;
24175        // }
24176         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24177         if(Roo.isSafari){ // safari
24178             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24179             v =  (v < 10) ? 10 : v;
24180             v =  (v > 48) ? 48 : v;
24181             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24182             
24183         }
24184         
24185         
24186         v = Math.max(1, v+adjust);
24187         
24188         this.execCmd('FontSize', v  );
24189     },
24190
24191     onEditorEvent : function(e)
24192     {
24193         this.owner.fireEvent('editorevent', this, e);
24194       //  this.updateToolbar();
24195         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24196     },
24197
24198     insertTag : function(tg)
24199     {
24200         // could be a bit smarter... -> wrap the current selected tRoo..
24201         if (tg.toLowerCase() == 'span' ||
24202             tg.toLowerCase() == 'code' ||
24203             tg.toLowerCase() == 'sup' ||
24204             tg.toLowerCase() == 'sub' 
24205             ) {
24206             
24207             range = this.createRange(this.getSelection());
24208             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24209             wrappingNode.appendChild(range.extractContents());
24210             range.insertNode(wrappingNode);
24211
24212             return;
24213             
24214             
24215             
24216         }
24217         this.execCmd("formatblock",   tg);
24218         
24219     },
24220     
24221     insertText : function(txt)
24222     {
24223         
24224         
24225         var range = this.createRange();
24226         range.deleteContents();
24227                //alert(Sender.getAttribute('label'));
24228                
24229         range.insertNode(this.doc.createTextNode(txt));
24230     } ,
24231     
24232      
24233
24234     /**
24235      * Executes a Midas editor command on the editor document and performs necessary focus and
24236      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24237      * @param {String} cmd The Midas command
24238      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24239      */
24240     relayCmd : function(cmd, value){
24241         this.win.focus();
24242         this.execCmd(cmd, value);
24243         this.owner.fireEvent('editorevent', this);
24244         //this.updateToolbar();
24245         this.owner.deferFocus();
24246     },
24247
24248     /**
24249      * Executes a Midas editor command directly on the editor document.
24250      * For visual commands, you should use {@link #relayCmd} instead.
24251      * <b>This should only be called after the editor is initialized.</b>
24252      * @param {String} cmd The Midas command
24253      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24254      */
24255     execCmd : function(cmd, value){
24256         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24257         this.syncValue();
24258     },
24259  
24260  
24261    
24262     /**
24263      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24264      * to insert tRoo.
24265      * @param {String} text | dom node.. 
24266      */
24267     insertAtCursor : function(text)
24268     {
24269         
24270         if(!this.activated){
24271             return;
24272         }
24273         /*
24274         if(Roo.isIE){
24275             this.win.focus();
24276             var r = this.doc.selection.createRange();
24277             if(r){
24278                 r.collapse(true);
24279                 r.pasteHTML(text);
24280                 this.syncValue();
24281                 this.deferFocus();
24282             
24283             }
24284             return;
24285         }
24286         */
24287         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24288             this.win.focus();
24289             
24290             
24291             // from jquery ui (MIT licenced)
24292             var range, node;
24293             var win = this.win;
24294             
24295             if (win.getSelection && win.getSelection().getRangeAt) {
24296                 range = win.getSelection().getRangeAt(0);
24297                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24298                 range.insertNode(node);
24299             } else if (win.document.selection && win.document.selection.createRange) {
24300                 // no firefox support
24301                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24302                 win.document.selection.createRange().pasteHTML(txt);
24303             } else {
24304                 // no firefox support
24305                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24306                 this.execCmd('InsertHTML', txt);
24307             } 
24308             
24309             this.syncValue();
24310             
24311             this.deferFocus();
24312         }
24313     },
24314  // private
24315     mozKeyPress : function(e){
24316         if(e.ctrlKey){
24317             var c = e.getCharCode(), cmd;
24318           
24319             if(c > 0){
24320                 c = String.fromCharCode(c).toLowerCase();
24321                 switch(c){
24322                     case 'b':
24323                         cmd = 'bold';
24324                         break;
24325                     case 'i':
24326                         cmd = 'italic';
24327                         break;
24328                     
24329                     case 'u':
24330                         cmd = 'underline';
24331                         break;
24332                     
24333                     case 'v':
24334                         this.cleanUpPaste.defer(100, this);
24335                         return;
24336                         
24337                 }
24338                 if(cmd){
24339                     this.win.focus();
24340                     this.execCmd(cmd);
24341                     this.deferFocus();
24342                     e.preventDefault();
24343                 }
24344                 
24345             }
24346         }
24347     },
24348
24349     // private
24350     fixKeys : function(){ // load time branching for fastest keydown performance
24351         if(Roo.isIE){
24352             return function(e){
24353                 var k = e.getKey(), r;
24354                 if(k == e.TAB){
24355                     e.stopEvent();
24356                     r = this.doc.selection.createRange();
24357                     if(r){
24358                         r.collapse(true);
24359                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24360                         this.deferFocus();
24361                     }
24362                     return;
24363                 }
24364                 
24365                 if(k == e.ENTER){
24366                     r = this.doc.selection.createRange();
24367                     if(r){
24368                         var target = r.parentElement();
24369                         if(!target || target.tagName.toLowerCase() != 'li'){
24370                             e.stopEvent();
24371                             r.pasteHTML('<br />');
24372                             r.collapse(false);
24373                             r.select();
24374                         }
24375                     }
24376                 }
24377                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24378                     this.cleanUpPaste.defer(100, this);
24379                     return;
24380                 }
24381                 
24382                 
24383             };
24384         }else if(Roo.isOpera){
24385             return function(e){
24386                 var k = e.getKey();
24387                 if(k == e.TAB){
24388                     e.stopEvent();
24389                     this.win.focus();
24390                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24391                     this.deferFocus();
24392                 }
24393                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24394                     this.cleanUpPaste.defer(100, this);
24395                     return;
24396                 }
24397                 
24398             };
24399         }else if(Roo.isSafari){
24400             return function(e){
24401                 var k = e.getKey();
24402                 
24403                 if(k == e.TAB){
24404                     e.stopEvent();
24405                     this.execCmd('InsertText','\t');
24406                     this.deferFocus();
24407                     return;
24408                 }
24409                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24410                     this.cleanUpPaste.defer(100, this);
24411                     return;
24412                 }
24413                 
24414              };
24415         }
24416     }(),
24417     
24418     getAllAncestors: function()
24419     {
24420         var p = this.getSelectedNode();
24421         var a = [];
24422         if (!p) {
24423             a.push(p); // push blank onto stack..
24424             p = this.getParentElement();
24425         }
24426         
24427         
24428         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24429             a.push(p);
24430             p = p.parentNode;
24431         }
24432         a.push(this.doc.body);
24433         return a;
24434     },
24435     lastSel : false,
24436     lastSelNode : false,
24437     
24438     
24439     getSelection : function() 
24440     {
24441         this.assignDocWin();
24442         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24443     },
24444     
24445     getSelectedNode: function() 
24446     {
24447         // this may only work on Gecko!!!
24448         
24449         // should we cache this!!!!
24450         
24451         
24452         
24453          
24454         var range = this.createRange(this.getSelection()).cloneRange();
24455         
24456         if (Roo.isIE) {
24457             var parent = range.parentElement();
24458             while (true) {
24459                 var testRange = range.duplicate();
24460                 testRange.moveToElementText(parent);
24461                 if (testRange.inRange(range)) {
24462                     break;
24463                 }
24464                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24465                     break;
24466                 }
24467                 parent = parent.parentElement;
24468             }
24469             return parent;
24470         }
24471         
24472         // is ancestor a text element.
24473         var ac =  range.commonAncestorContainer;
24474         if (ac.nodeType == 3) {
24475             ac = ac.parentNode;
24476         }
24477         
24478         var ar = ac.childNodes;
24479          
24480         var nodes = [];
24481         var other_nodes = [];
24482         var has_other_nodes = false;
24483         for (var i=0;i<ar.length;i++) {
24484             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24485                 continue;
24486             }
24487             // fullly contained node.
24488             
24489             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24490                 nodes.push(ar[i]);
24491                 continue;
24492             }
24493             
24494             // probably selected..
24495             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24496                 other_nodes.push(ar[i]);
24497                 continue;
24498             }
24499             // outer..
24500             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24501                 continue;
24502             }
24503             
24504             
24505             has_other_nodes = true;
24506         }
24507         if (!nodes.length && other_nodes.length) {
24508             nodes= other_nodes;
24509         }
24510         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24511             return false;
24512         }
24513         
24514         return nodes[0];
24515     },
24516     createRange: function(sel)
24517     {
24518         // this has strange effects when using with 
24519         // top toolbar - not sure if it's a great idea.
24520         //this.editor.contentWindow.focus();
24521         if (typeof sel != "undefined") {
24522             try {
24523                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24524             } catch(e) {
24525                 return this.doc.createRange();
24526             }
24527         } else {
24528             return this.doc.createRange();
24529         }
24530     },
24531     getParentElement: function()
24532     {
24533         
24534         this.assignDocWin();
24535         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24536         
24537         var range = this.createRange(sel);
24538          
24539         try {
24540             var p = range.commonAncestorContainer;
24541             while (p.nodeType == 3) { // text node
24542                 p = p.parentNode;
24543             }
24544             return p;
24545         } catch (e) {
24546             return null;
24547         }
24548     
24549     },
24550     /***
24551      *
24552      * Range intersection.. the hard stuff...
24553      *  '-1' = before
24554      *  '0' = hits..
24555      *  '1' = after.
24556      *         [ -- selected range --- ]
24557      *   [fail]                        [fail]
24558      *
24559      *    basically..
24560      *      if end is before start or  hits it. fail.
24561      *      if start is after end or hits it fail.
24562      *
24563      *   if either hits (but other is outside. - then it's not 
24564      *   
24565      *    
24566      **/
24567     
24568     
24569     // @see http://www.thismuchiknow.co.uk/?p=64.
24570     rangeIntersectsNode : function(range, node)
24571     {
24572         var nodeRange = node.ownerDocument.createRange();
24573         try {
24574             nodeRange.selectNode(node);
24575         } catch (e) {
24576             nodeRange.selectNodeContents(node);
24577         }
24578     
24579         var rangeStartRange = range.cloneRange();
24580         rangeStartRange.collapse(true);
24581     
24582         var rangeEndRange = range.cloneRange();
24583         rangeEndRange.collapse(false);
24584     
24585         var nodeStartRange = nodeRange.cloneRange();
24586         nodeStartRange.collapse(true);
24587     
24588         var nodeEndRange = nodeRange.cloneRange();
24589         nodeEndRange.collapse(false);
24590     
24591         return rangeStartRange.compareBoundaryPoints(
24592                  Range.START_TO_START, nodeEndRange) == -1 &&
24593                rangeEndRange.compareBoundaryPoints(
24594                  Range.START_TO_START, nodeStartRange) == 1;
24595         
24596          
24597     },
24598     rangeCompareNode : function(range, node)
24599     {
24600         var nodeRange = node.ownerDocument.createRange();
24601         try {
24602             nodeRange.selectNode(node);
24603         } catch (e) {
24604             nodeRange.selectNodeContents(node);
24605         }
24606         
24607         
24608         range.collapse(true);
24609     
24610         nodeRange.collapse(true);
24611      
24612         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24613         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24614          
24615         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24616         
24617         var nodeIsBefore   =  ss == 1;
24618         var nodeIsAfter    = ee == -1;
24619         
24620         if (nodeIsBefore && nodeIsAfter) {
24621             return 0; // outer
24622         }
24623         if (!nodeIsBefore && nodeIsAfter) {
24624             return 1; //right trailed.
24625         }
24626         
24627         if (nodeIsBefore && !nodeIsAfter) {
24628             return 2;  // left trailed.
24629         }
24630         // fully contined.
24631         return 3;
24632     },
24633
24634     // private? - in a new class?
24635     cleanUpPaste :  function()
24636     {
24637         // cleans up the whole document..
24638         Roo.log('cleanuppaste');
24639         
24640         this.cleanUpChildren(this.doc.body);
24641         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24642         if (clean != this.doc.body.innerHTML) {
24643             this.doc.body.innerHTML = clean;
24644         }
24645         
24646     },
24647     
24648     cleanWordChars : function(input) {// change the chars to hex code
24649         var he = Roo.HtmlEditorCore;
24650         
24651         var output = input;
24652         Roo.each(he.swapCodes, function(sw) { 
24653             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24654             
24655             output = output.replace(swapper, sw[1]);
24656         });
24657         
24658         return output;
24659     },
24660     
24661     
24662     cleanUpChildren : function (n)
24663     {
24664         if (!n.childNodes.length) {
24665             return;
24666         }
24667         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24668            this.cleanUpChild(n.childNodes[i]);
24669         }
24670     },
24671     
24672     
24673         
24674     
24675     cleanUpChild : function (node)
24676     {
24677         var ed = this;
24678         //console.log(node);
24679         if (node.nodeName == "#text") {
24680             // clean up silly Windows -- stuff?
24681             return; 
24682         }
24683         if (node.nodeName == "#comment") {
24684             node.parentNode.removeChild(node);
24685             // clean up silly Windows -- stuff?
24686             return; 
24687         }
24688         var lcname = node.tagName.toLowerCase();
24689         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24690         // whitelist of tags..
24691         
24692         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24693             // remove node.
24694             node.parentNode.removeChild(node);
24695             return;
24696             
24697         }
24698         
24699         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24700         
24701         // spans with no attributes - just remove them..
24702         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24703             remove_keep_children = true;
24704         }
24705         
24706         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24707         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24708         
24709         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24710         //    remove_keep_children = true;
24711         //}
24712         
24713         if (remove_keep_children) {
24714             this.cleanUpChildren(node);
24715             // inserts everything just before this node...
24716             while (node.childNodes.length) {
24717                 var cn = node.childNodes[0];
24718                 node.removeChild(cn);
24719                 node.parentNode.insertBefore(cn, node);
24720             }
24721             node.parentNode.removeChild(node);
24722             return;
24723         }
24724         
24725         if (!node.attributes || !node.attributes.length) {
24726             
24727           
24728             
24729             
24730             this.cleanUpChildren(node);
24731             return;
24732         }
24733         
24734         function cleanAttr(n,v)
24735         {
24736             
24737             if (v.match(/^\./) || v.match(/^\//)) {
24738                 return;
24739             }
24740             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24741                 return;
24742             }
24743             if (v.match(/^#/)) {
24744                 return;
24745             }
24746             if (v.match(/^\{/)) { // allow template editing.
24747                 return;
24748             }
24749 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24750             node.removeAttribute(n);
24751             
24752         }
24753         
24754         var cwhite = this.cwhite;
24755         var cblack = this.cblack;
24756             
24757         function cleanStyle(n,v)
24758         {
24759             if (v.match(/expression/)) { //XSS?? should we even bother..
24760                 node.removeAttribute(n);
24761                 return;
24762             }
24763             
24764             var parts = v.split(/;/);
24765             var clean = [];
24766             
24767             Roo.each(parts, function(p) {
24768                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24769                 if (!p.length) {
24770                     return true;
24771                 }
24772                 var l = p.split(':').shift().replace(/\s+/g,'');
24773                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24774                 
24775                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24776 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24777                     //node.removeAttribute(n);
24778                     return true;
24779                 }
24780                 //Roo.log()
24781                 // only allow 'c whitelisted system attributes'
24782                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24783 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24784                     //node.removeAttribute(n);
24785                     return true;
24786                 }
24787                 
24788                 
24789                  
24790                 
24791                 clean.push(p);
24792                 return true;
24793             });
24794             if (clean.length) { 
24795                 node.setAttribute(n, clean.join(';'));
24796             } else {
24797                 node.removeAttribute(n);
24798             }
24799             
24800         }
24801         
24802         
24803         for (var i = node.attributes.length-1; i > -1 ; i--) {
24804             var a = node.attributes[i];
24805             //console.log(a);
24806             
24807             if (a.name.toLowerCase().substr(0,2)=='on')  {
24808                 node.removeAttribute(a.name);
24809                 continue;
24810             }
24811             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24812                 node.removeAttribute(a.name);
24813                 continue;
24814             }
24815             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24816                 cleanAttr(a.name,a.value); // fixme..
24817                 continue;
24818             }
24819             if (a.name == 'style') {
24820                 cleanStyle(a.name,a.value);
24821                 continue;
24822             }
24823             /// clean up MS crap..
24824             // tecnically this should be a list of valid class'es..
24825             
24826             
24827             if (a.name == 'class') {
24828                 if (a.value.match(/^Mso/)) {
24829                     node.removeAttribute('class');
24830                 }
24831                 
24832                 if (a.value.match(/^body$/)) {
24833                     node.removeAttribute('class');
24834                 }
24835                 continue;
24836             }
24837             
24838             // style cleanup!?
24839             // class cleanup?
24840             
24841         }
24842         
24843         
24844         this.cleanUpChildren(node);
24845         
24846         
24847     },
24848     
24849     /**
24850      * Clean up MS wordisms...
24851      */
24852     cleanWord : function(node)
24853     {
24854         if (!node) {
24855             this.cleanWord(this.doc.body);
24856             return;
24857         }
24858         
24859         if(
24860                 node.nodeName == 'SPAN' &&
24861                 !node.hasAttributes() &&
24862                 node.childNodes.length == 1 &&
24863                 node.firstChild.nodeName == "#text"  
24864         ) {
24865             var textNode = node.firstChild;
24866             node.removeChild(textNode);
24867             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24868                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24869             }
24870             node.parentNode.insertBefore(textNode, node);
24871             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24872                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24873             }
24874             node.parentNode.removeChild(node);
24875         }
24876         
24877         if (node.nodeName == "#text") {
24878             // clean up silly Windows -- stuff?
24879             return; 
24880         }
24881         if (node.nodeName == "#comment") {
24882             node.parentNode.removeChild(node);
24883             // clean up silly Windows -- stuff?
24884             return; 
24885         }
24886         
24887         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24888             node.parentNode.removeChild(node);
24889             return;
24890         }
24891         //Roo.log(node.tagName);
24892         // remove - but keep children..
24893         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24894             //Roo.log('-- removed');
24895             while (node.childNodes.length) {
24896                 var cn = node.childNodes[0];
24897                 node.removeChild(cn);
24898                 node.parentNode.insertBefore(cn, node);
24899                 // move node to parent - and clean it..
24900                 this.cleanWord(cn);
24901             }
24902             node.parentNode.removeChild(node);
24903             /// no need to iterate chidlren = it's got none..
24904             //this.iterateChildren(node, this.cleanWord);
24905             return;
24906         }
24907         // clean styles
24908         if (node.className.length) {
24909             
24910             var cn = node.className.split(/\W+/);
24911             var cna = [];
24912             Roo.each(cn, function(cls) {
24913                 if (cls.match(/Mso[a-zA-Z]+/)) {
24914                     return;
24915                 }
24916                 cna.push(cls);
24917             });
24918             node.className = cna.length ? cna.join(' ') : '';
24919             if (!cna.length) {
24920                 node.removeAttribute("class");
24921             }
24922         }
24923         
24924         if (node.hasAttribute("lang")) {
24925             node.removeAttribute("lang");
24926         }
24927         
24928         if (node.hasAttribute("style")) {
24929             
24930             var styles = node.getAttribute("style").split(";");
24931             var nstyle = [];
24932             Roo.each(styles, function(s) {
24933                 if (!s.match(/:/)) {
24934                     return;
24935                 }
24936                 var kv = s.split(":");
24937                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24938                     return;
24939                 }
24940                 // what ever is left... we allow.
24941                 nstyle.push(s);
24942             });
24943             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24944             if (!nstyle.length) {
24945                 node.removeAttribute('style');
24946             }
24947         }
24948         this.iterateChildren(node, this.cleanWord);
24949         
24950         
24951         
24952     },
24953     /**
24954      * iterateChildren of a Node, calling fn each time, using this as the scole..
24955      * @param {DomNode} node node to iterate children of.
24956      * @param {Function} fn method of this class to call on each item.
24957      */
24958     iterateChildren : function(node, fn)
24959     {
24960         if (!node.childNodes.length) {
24961                 return;
24962         }
24963         for (var i = node.childNodes.length-1; i > -1 ; i--) {
24964            fn.call(this, node.childNodes[i])
24965         }
24966     },
24967     
24968     
24969     /**
24970      * cleanTableWidths.
24971      *
24972      * Quite often pasting from word etc.. results in tables with column and widths.
24973      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24974      *
24975      */
24976     cleanTableWidths : function(node)
24977     {
24978          
24979          
24980         if (!node) {
24981             this.cleanTableWidths(this.doc.body);
24982             return;
24983         }
24984         
24985         // ignore list...
24986         if (node.nodeName == "#text" || node.nodeName == "#comment") {
24987             return; 
24988         }
24989         Roo.log(node.tagName);
24990         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24991             this.iterateChildren(node, this.cleanTableWidths);
24992             return;
24993         }
24994         if (node.hasAttribute('width')) {
24995             node.removeAttribute('width');
24996         }
24997         
24998          
24999         if (node.hasAttribute("style")) {
25000             // pretty basic...
25001             
25002             var styles = node.getAttribute("style").split(";");
25003             var nstyle = [];
25004             Roo.each(styles, function(s) {
25005                 if (!s.match(/:/)) {
25006                     return;
25007                 }
25008                 var kv = s.split(":");
25009                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25010                     return;
25011                 }
25012                 // what ever is left... we allow.
25013                 nstyle.push(s);
25014             });
25015             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25016             if (!nstyle.length) {
25017                 node.removeAttribute('style');
25018             }
25019         }
25020         
25021         this.iterateChildren(node, this.cleanTableWidths);
25022         
25023         
25024     },
25025     
25026     
25027     
25028     
25029     domToHTML : function(currentElement, depth, nopadtext) {
25030         
25031         depth = depth || 0;
25032         nopadtext = nopadtext || false;
25033     
25034         if (!currentElement) {
25035             return this.domToHTML(this.doc.body);
25036         }
25037         
25038         //Roo.log(currentElement);
25039         var j;
25040         var allText = false;
25041         var nodeName = currentElement.nodeName;
25042         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25043         
25044         if  (nodeName == '#text') {
25045             
25046             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25047         }
25048         
25049         
25050         var ret = '';
25051         if (nodeName != 'BODY') {
25052              
25053             var i = 0;
25054             // Prints the node tagName, such as <A>, <IMG>, etc
25055             if (tagName) {
25056                 var attr = [];
25057                 for(i = 0; i < currentElement.attributes.length;i++) {
25058                     // quoting?
25059                     var aname = currentElement.attributes.item(i).name;
25060                     if (!currentElement.attributes.item(i).value.length) {
25061                         continue;
25062                     }
25063                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25064                 }
25065                 
25066                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25067             } 
25068             else {
25069                 
25070                 // eack
25071             }
25072         } else {
25073             tagName = false;
25074         }
25075         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25076             return ret;
25077         }
25078         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25079             nopadtext = true;
25080         }
25081         
25082         
25083         // Traverse the tree
25084         i = 0;
25085         var currentElementChild = currentElement.childNodes.item(i);
25086         var allText = true;
25087         var innerHTML  = '';
25088         lastnode = '';
25089         while (currentElementChild) {
25090             // Formatting code (indent the tree so it looks nice on the screen)
25091             var nopad = nopadtext;
25092             if (lastnode == 'SPAN') {
25093                 nopad  = true;
25094             }
25095             // text
25096             if  (currentElementChild.nodeName == '#text') {
25097                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25098                 toadd = nopadtext ? toadd : toadd.trim();
25099                 if (!nopad && toadd.length > 80) {
25100                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25101                 }
25102                 innerHTML  += toadd;
25103                 
25104                 i++;
25105                 currentElementChild = currentElement.childNodes.item(i);
25106                 lastNode = '';
25107                 continue;
25108             }
25109             allText = false;
25110             
25111             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25112                 
25113             // Recursively traverse the tree structure of the child node
25114             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25115             lastnode = currentElementChild.nodeName;
25116             i++;
25117             currentElementChild=currentElement.childNodes.item(i);
25118         }
25119         
25120         ret += innerHTML;
25121         
25122         if (!allText) {
25123                 // The remaining code is mostly for formatting the tree
25124             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25125         }
25126         
25127         
25128         if (tagName) {
25129             ret+= "</"+tagName+">";
25130         }
25131         return ret;
25132         
25133     },
25134         
25135     applyBlacklists : function()
25136     {
25137         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25138         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25139         
25140         this.white = [];
25141         this.black = [];
25142         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25143             if (b.indexOf(tag) > -1) {
25144                 return;
25145             }
25146             this.white.push(tag);
25147             
25148         }, this);
25149         
25150         Roo.each(w, function(tag) {
25151             if (b.indexOf(tag) > -1) {
25152                 return;
25153             }
25154             if (this.white.indexOf(tag) > -1) {
25155                 return;
25156             }
25157             this.white.push(tag);
25158             
25159         }, this);
25160         
25161         
25162         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25163             if (w.indexOf(tag) > -1) {
25164                 return;
25165             }
25166             this.black.push(tag);
25167             
25168         }, this);
25169         
25170         Roo.each(b, function(tag) {
25171             if (w.indexOf(tag) > -1) {
25172                 return;
25173             }
25174             if (this.black.indexOf(tag) > -1) {
25175                 return;
25176             }
25177             this.black.push(tag);
25178             
25179         }, this);
25180         
25181         
25182         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25183         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25184         
25185         this.cwhite = [];
25186         this.cblack = [];
25187         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25188             if (b.indexOf(tag) > -1) {
25189                 return;
25190             }
25191             this.cwhite.push(tag);
25192             
25193         }, this);
25194         
25195         Roo.each(w, function(tag) {
25196             if (b.indexOf(tag) > -1) {
25197                 return;
25198             }
25199             if (this.cwhite.indexOf(tag) > -1) {
25200                 return;
25201             }
25202             this.cwhite.push(tag);
25203             
25204         }, this);
25205         
25206         
25207         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25208             if (w.indexOf(tag) > -1) {
25209                 return;
25210             }
25211             this.cblack.push(tag);
25212             
25213         }, this);
25214         
25215         Roo.each(b, function(tag) {
25216             if (w.indexOf(tag) > -1) {
25217                 return;
25218             }
25219             if (this.cblack.indexOf(tag) > -1) {
25220                 return;
25221             }
25222             this.cblack.push(tag);
25223             
25224         }, this);
25225     },
25226     
25227     setStylesheets : function(stylesheets)
25228     {
25229         if(typeof(stylesheets) == 'string'){
25230             Roo.get(this.iframe.contentDocument.head).createChild({
25231                 tag : 'link',
25232                 rel : 'stylesheet',
25233                 type : 'text/css',
25234                 href : stylesheets
25235             });
25236             
25237             return;
25238         }
25239         var _this = this;
25240      
25241         Roo.each(stylesheets, function(s) {
25242             if(!s.length){
25243                 return;
25244             }
25245             
25246             Roo.get(_this.iframe.contentDocument.head).createChild({
25247                 tag : 'link',
25248                 rel : 'stylesheet',
25249                 type : 'text/css',
25250                 href : s
25251             });
25252         });
25253
25254         
25255     },
25256     
25257     removeStylesheets : function()
25258     {
25259         var _this = this;
25260         
25261         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25262             s.remove();
25263         });
25264     },
25265     
25266     setStyle : function(style)
25267     {
25268         Roo.get(this.iframe.contentDocument.head).createChild({
25269             tag : 'style',
25270             type : 'text/css',
25271             html : style
25272         });
25273
25274         return;
25275     }
25276     
25277     // hide stuff that is not compatible
25278     /**
25279      * @event blur
25280      * @hide
25281      */
25282     /**
25283      * @event change
25284      * @hide
25285      */
25286     /**
25287      * @event focus
25288      * @hide
25289      */
25290     /**
25291      * @event specialkey
25292      * @hide
25293      */
25294     /**
25295      * @cfg {String} fieldClass @hide
25296      */
25297     /**
25298      * @cfg {String} focusClass @hide
25299      */
25300     /**
25301      * @cfg {String} autoCreate @hide
25302      */
25303     /**
25304      * @cfg {String} inputType @hide
25305      */
25306     /**
25307      * @cfg {String} invalidClass @hide
25308      */
25309     /**
25310      * @cfg {String} invalidText @hide
25311      */
25312     /**
25313      * @cfg {String} msgFx @hide
25314      */
25315     /**
25316      * @cfg {String} validateOnBlur @hide
25317      */
25318 });
25319
25320 Roo.HtmlEditorCore.white = [
25321         'area', 'br', 'img', 'input', 'hr', 'wbr',
25322         
25323        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25324        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25325        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25326        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25327        'table',   'ul',         'xmp', 
25328        
25329        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25330       'thead',   'tr', 
25331      
25332       'dir', 'menu', 'ol', 'ul', 'dl',
25333        
25334       'embed',  'object'
25335 ];
25336
25337
25338 Roo.HtmlEditorCore.black = [
25339     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25340         'applet', // 
25341         'base',   'basefont', 'bgsound', 'blink',  'body', 
25342         'frame',  'frameset', 'head',    'html',   'ilayer', 
25343         'iframe', 'layer',  'link',     'meta',    'object',   
25344         'script', 'style' ,'title',  'xml' // clean later..
25345 ];
25346 Roo.HtmlEditorCore.clean = [
25347     'script', 'style', 'title', 'xml'
25348 ];
25349 Roo.HtmlEditorCore.remove = [
25350     'font'
25351 ];
25352 // attributes..
25353
25354 Roo.HtmlEditorCore.ablack = [
25355     'on'
25356 ];
25357     
25358 Roo.HtmlEditorCore.aclean = [ 
25359     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25360 ];
25361
25362 // protocols..
25363 Roo.HtmlEditorCore.pwhite= [
25364         'http',  'https',  'mailto'
25365 ];
25366
25367 // white listed style attributes.
25368 Roo.HtmlEditorCore.cwhite= [
25369       //  'text-align', /// default is to allow most things..
25370       
25371          
25372 //        'font-size'//??
25373 ];
25374
25375 // black listed style attributes.
25376 Roo.HtmlEditorCore.cblack= [
25377       //  'font-size' -- this can be set by the project 
25378 ];
25379
25380
25381 Roo.HtmlEditorCore.swapCodes   =[ 
25382     [    8211, "--" ], 
25383     [    8212, "--" ], 
25384     [    8216,  "'" ],  
25385     [    8217, "'" ],  
25386     [    8220, '"' ],  
25387     [    8221, '"' ],  
25388     [    8226, "*" ],  
25389     [    8230, "..." ]
25390 ]; 
25391
25392     /*
25393  * - LGPL
25394  *
25395  * HtmlEditor
25396  * 
25397  */
25398
25399 /**
25400  * @class Roo.bootstrap.HtmlEditor
25401  * @extends Roo.bootstrap.TextArea
25402  * Bootstrap HtmlEditor class
25403
25404  * @constructor
25405  * Create a new HtmlEditor
25406  * @param {Object} config The config object
25407  */
25408
25409 Roo.bootstrap.HtmlEditor = function(config){
25410     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25411     if (!this.toolbars) {
25412         this.toolbars = [];
25413     }
25414     
25415     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25416     this.addEvents({
25417             /**
25418              * @event initialize
25419              * Fires when the editor is fully initialized (including the iframe)
25420              * @param {HtmlEditor} this
25421              */
25422             initialize: true,
25423             /**
25424              * @event activate
25425              * Fires when the editor is first receives the focus. Any insertion must wait
25426              * until after this event.
25427              * @param {HtmlEditor} this
25428              */
25429             activate: true,
25430              /**
25431              * @event beforesync
25432              * Fires before the textarea is updated with content from the editor iframe. Return false
25433              * to cancel the sync.
25434              * @param {HtmlEditor} this
25435              * @param {String} html
25436              */
25437             beforesync: true,
25438              /**
25439              * @event beforepush
25440              * Fires before the iframe editor is updated with content from the textarea. Return false
25441              * to cancel the push.
25442              * @param {HtmlEditor} this
25443              * @param {String} html
25444              */
25445             beforepush: true,
25446              /**
25447              * @event sync
25448              * Fires when the textarea is updated with content from the editor iframe.
25449              * @param {HtmlEditor} this
25450              * @param {String} html
25451              */
25452             sync: true,
25453              /**
25454              * @event push
25455              * Fires when the iframe editor is updated with content from the textarea.
25456              * @param {HtmlEditor} this
25457              * @param {String} html
25458              */
25459             push: true,
25460              /**
25461              * @event editmodechange
25462              * Fires when the editor switches edit modes
25463              * @param {HtmlEditor} this
25464              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25465              */
25466             editmodechange: true,
25467             /**
25468              * @event editorevent
25469              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25470              * @param {HtmlEditor} this
25471              */
25472             editorevent: true,
25473             /**
25474              * @event firstfocus
25475              * Fires when on first focus - needed by toolbars..
25476              * @param {HtmlEditor} this
25477              */
25478             firstfocus: true,
25479             /**
25480              * @event autosave
25481              * Auto save the htmlEditor value as a file into Events
25482              * @param {HtmlEditor} this
25483              */
25484             autosave: true,
25485             /**
25486              * @event savedpreview
25487              * preview the saved version of htmlEditor
25488              * @param {HtmlEditor} this
25489              */
25490             savedpreview: true
25491         });
25492 };
25493
25494
25495 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25496     
25497     
25498       /**
25499      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25500      */
25501     toolbars : false,
25502     
25503      /**
25504     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25505     */
25506     btns : [],
25507    
25508      /**
25509      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25510      *                        Roo.resizable.
25511      */
25512     resizable : false,
25513      /**
25514      * @cfg {Number} height (in pixels)
25515      */   
25516     height: 300,
25517    /**
25518      * @cfg {Number} width (in pixels)
25519      */   
25520     width: false,
25521     
25522     /**
25523      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25524      * 
25525      */
25526     stylesheets: false,
25527     
25528     // id of frame..
25529     frameId: false,
25530     
25531     // private properties
25532     validationEvent : false,
25533     deferHeight: true,
25534     initialized : false,
25535     activated : false,
25536     
25537     onFocus : Roo.emptyFn,
25538     iframePad:3,
25539     hideMode:'offsets',
25540     
25541     tbContainer : false,
25542     
25543     bodyCls : '',
25544     
25545     toolbarContainer :function() {
25546         return this.wrap.select('.x-html-editor-tb',true).first();
25547     },
25548
25549     /**
25550      * Protected method that will not generally be called directly. It
25551      * is called when the editor creates its toolbar. Override this method if you need to
25552      * add custom toolbar buttons.
25553      * @param {HtmlEditor} editor
25554      */
25555     createToolbar : function(){
25556         Roo.log('renewing');
25557         Roo.log("create toolbars");
25558         
25559         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25560         this.toolbars[0].render(this.toolbarContainer());
25561         
25562         return;
25563         
25564 //        if (!editor.toolbars || !editor.toolbars.length) {
25565 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25566 //        }
25567 //        
25568 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25569 //            editor.toolbars[i] = Roo.factory(
25570 //                    typeof(editor.toolbars[i]) == 'string' ?
25571 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25572 //                Roo.bootstrap.HtmlEditor);
25573 //            editor.toolbars[i].init(editor);
25574 //        }
25575     },
25576
25577      
25578     // private
25579     onRender : function(ct, position)
25580     {
25581        // Roo.log("Call onRender: " + this.xtype);
25582         var _t = this;
25583         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25584       
25585         this.wrap = this.inputEl().wrap({
25586             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25587         });
25588         
25589         this.editorcore.onRender(ct, position);
25590          
25591         if (this.resizable) {
25592             this.resizeEl = new Roo.Resizable(this.wrap, {
25593                 pinned : true,
25594                 wrap: true,
25595                 dynamic : true,
25596                 minHeight : this.height,
25597                 height: this.height,
25598                 handles : this.resizable,
25599                 width: this.width,
25600                 listeners : {
25601                     resize : function(r, w, h) {
25602                         _t.onResize(w,h); // -something
25603                     }
25604                 }
25605             });
25606             
25607         }
25608         this.createToolbar(this);
25609        
25610         
25611         if(!this.width && this.resizable){
25612             this.setSize(this.wrap.getSize());
25613         }
25614         if (this.resizeEl) {
25615             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25616             // should trigger onReize..
25617         }
25618         
25619     },
25620
25621     // private
25622     onResize : function(w, h)
25623     {
25624         Roo.log('resize: ' +w + ',' + h );
25625         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25626         var ew = false;
25627         var eh = false;
25628         
25629         if(this.inputEl() ){
25630             if(typeof w == 'number'){
25631                 var aw = w - this.wrap.getFrameWidth('lr');
25632                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25633                 ew = aw;
25634             }
25635             if(typeof h == 'number'){
25636                  var tbh = -11;  // fixme it needs to tool bar size!
25637                 for (var i =0; i < this.toolbars.length;i++) {
25638                     // fixme - ask toolbars for heights?
25639                     tbh += this.toolbars[i].el.getHeight();
25640                     //if (this.toolbars[i].footer) {
25641                     //    tbh += this.toolbars[i].footer.el.getHeight();
25642                     //}
25643                 }
25644               
25645                 
25646                 
25647                 
25648                 
25649                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25650                 ah -= 5; // knock a few pixes off for look..
25651                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25652                 var eh = ah;
25653             }
25654         }
25655         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25656         this.editorcore.onResize(ew,eh);
25657         
25658     },
25659
25660     /**
25661      * Toggles the editor between standard and source edit mode.
25662      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25663      */
25664     toggleSourceEdit : function(sourceEditMode)
25665     {
25666         this.editorcore.toggleSourceEdit(sourceEditMode);
25667         
25668         if(this.editorcore.sourceEditMode){
25669             Roo.log('editor - showing textarea');
25670             
25671 //            Roo.log('in');
25672 //            Roo.log(this.syncValue());
25673             this.syncValue();
25674             this.inputEl().removeClass(['hide', 'x-hidden']);
25675             this.inputEl().dom.removeAttribute('tabIndex');
25676             this.inputEl().focus();
25677         }else{
25678             Roo.log('editor - hiding textarea');
25679 //            Roo.log('out')
25680 //            Roo.log(this.pushValue()); 
25681             this.pushValue();
25682             
25683             this.inputEl().addClass(['hide', 'x-hidden']);
25684             this.inputEl().dom.setAttribute('tabIndex', -1);
25685             //this.deferFocus();
25686         }
25687          
25688         if(this.resizable){
25689             this.setSize(this.wrap.getSize());
25690         }
25691         
25692         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25693     },
25694  
25695     // private (for BoxComponent)
25696     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25697
25698     // private (for BoxComponent)
25699     getResizeEl : function(){
25700         return this.wrap;
25701     },
25702
25703     // private (for BoxComponent)
25704     getPositionEl : function(){
25705         return this.wrap;
25706     },
25707
25708     // private
25709     initEvents : function(){
25710         this.originalValue = this.getValue();
25711     },
25712
25713 //    /**
25714 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25715 //     * @method
25716 //     */
25717 //    markInvalid : Roo.emptyFn,
25718 //    /**
25719 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25720 //     * @method
25721 //     */
25722 //    clearInvalid : Roo.emptyFn,
25723
25724     setValue : function(v){
25725         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25726         this.editorcore.pushValue();
25727     },
25728
25729      
25730     // private
25731     deferFocus : function(){
25732         this.focus.defer(10, this);
25733     },
25734
25735     // doc'ed in Field
25736     focus : function(){
25737         this.editorcore.focus();
25738         
25739     },
25740       
25741
25742     // private
25743     onDestroy : function(){
25744         
25745         
25746         
25747         if(this.rendered){
25748             
25749             for (var i =0; i < this.toolbars.length;i++) {
25750                 // fixme - ask toolbars for heights?
25751                 this.toolbars[i].onDestroy();
25752             }
25753             
25754             this.wrap.dom.innerHTML = '';
25755             this.wrap.remove();
25756         }
25757     },
25758
25759     // private
25760     onFirstFocus : function(){
25761         //Roo.log("onFirstFocus");
25762         this.editorcore.onFirstFocus();
25763          for (var i =0; i < this.toolbars.length;i++) {
25764             this.toolbars[i].onFirstFocus();
25765         }
25766         
25767     },
25768     
25769     // private
25770     syncValue : function()
25771     {   
25772         this.editorcore.syncValue();
25773     },
25774     
25775     pushValue : function()
25776     {   
25777         this.editorcore.pushValue();
25778     }
25779      
25780     
25781     // hide stuff that is not compatible
25782     /**
25783      * @event blur
25784      * @hide
25785      */
25786     /**
25787      * @event change
25788      * @hide
25789      */
25790     /**
25791      * @event focus
25792      * @hide
25793      */
25794     /**
25795      * @event specialkey
25796      * @hide
25797      */
25798     /**
25799      * @cfg {String} fieldClass @hide
25800      */
25801     /**
25802      * @cfg {String} focusClass @hide
25803      */
25804     /**
25805      * @cfg {String} autoCreate @hide
25806      */
25807     /**
25808      * @cfg {String} inputType @hide
25809      */
25810      
25811     /**
25812      * @cfg {String} invalidText @hide
25813      */
25814     /**
25815      * @cfg {String} msgFx @hide
25816      */
25817     /**
25818      * @cfg {String} validateOnBlur @hide
25819      */
25820 });
25821  
25822     
25823    
25824    
25825    
25826       
25827 Roo.namespace('Roo.bootstrap.htmleditor');
25828 /**
25829  * @class Roo.bootstrap.HtmlEditorToolbar1
25830  * Basic Toolbar
25831  * 
25832  * @example
25833  * Usage:
25834  *
25835  new Roo.bootstrap.HtmlEditor({
25836     ....
25837     toolbars : [
25838         new Roo.bootstrap.HtmlEditorToolbar1({
25839             disable : { fonts: 1 , format: 1, ..., ... , ...],
25840             btns : [ .... ]
25841         })
25842     }
25843      
25844  * 
25845  * @cfg {Object} disable List of elements to disable..
25846  * @cfg {Array} btns List of additional buttons.
25847  * 
25848  * 
25849  * NEEDS Extra CSS? 
25850  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25851  */
25852  
25853 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25854 {
25855     
25856     Roo.apply(this, config);
25857     
25858     // default disabled, based on 'good practice'..
25859     this.disable = this.disable || {};
25860     Roo.applyIf(this.disable, {
25861         fontSize : true,
25862         colors : true,
25863         specialElements : true
25864     });
25865     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25866     
25867     this.editor = config.editor;
25868     this.editorcore = config.editor.editorcore;
25869     
25870     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25871     
25872     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25873     // dont call parent... till later.
25874 }
25875 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
25876      
25877     bar : true,
25878     
25879     editor : false,
25880     editorcore : false,
25881     
25882     
25883     formats : [
25884         "p" ,  
25885         "h1","h2","h3","h4","h5","h6", 
25886         "pre", "code", 
25887         "abbr", "acronym", "address", "cite", "samp", "var",
25888         'div','span'
25889     ],
25890     
25891     onRender : function(ct, position)
25892     {
25893        // Roo.log("Call onRender: " + this.xtype);
25894         
25895        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25896        Roo.log(this.el);
25897        this.el.dom.style.marginBottom = '0';
25898        var _this = this;
25899        var editorcore = this.editorcore;
25900        var editor= this.editor;
25901        
25902        var children = [];
25903        var btn = function(id,cmd , toggle, handler, html){
25904        
25905             var  event = toggle ? 'toggle' : 'click';
25906        
25907             var a = {
25908                 size : 'sm',
25909                 xtype: 'Button',
25910                 xns: Roo.bootstrap,
25911                 //glyphicon : id,
25912                 fa: id,
25913                 cmd : id || cmd,
25914                 enableToggle:toggle !== false,
25915                 html : html || '',
25916                 pressed : toggle ? false : null,
25917                 listeners : {}
25918             };
25919             a.listeners[toggle ? 'toggle' : 'click'] = function() {
25920                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
25921             };
25922             children.push(a);
25923             return a;
25924        }
25925        
25926     //    var cb_box = function...
25927         
25928         var style = {
25929                 xtype: 'Button',
25930                 size : 'sm',
25931                 xns: Roo.bootstrap,
25932                 fa : 'font',
25933                 //html : 'submit'
25934                 menu : {
25935                     xtype: 'Menu',
25936                     xns: Roo.bootstrap,
25937                     items:  []
25938                 }
25939         };
25940         Roo.each(this.formats, function(f) {
25941             style.menu.items.push({
25942                 xtype :'MenuItem',
25943                 xns: Roo.bootstrap,
25944                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25945                 tagname : f,
25946                 listeners : {
25947                     click : function()
25948                     {
25949                         editorcore.insertTag(this.tagname);
25950                         editor.focus();
25951                     }
25952                 }
25953                 
25954             });
25955         });
25956         children.push(style);   
25957         
25958         btn('bold',false,true);
25959         btn('italic',false,true);
25960         btn('align-left', 'justifyleft',true);
25961         btn('align-center', 'justifycenter',true);
25962         btn('align-right' , 'justifyright',true);
25963         btn('link', false, false, function(btn) {
25964             //Roo.log("create link?");
25965             var url = prompt(this.createLinkText, this.defaultLinkValue);
25966             if(url && url != 'http:/'+'/'){
25967                 this.editorcore.relayCmd('createlink', url);
25968             }
25969         }),
25970         btn('list','insertunorderedlist',true);
25971         btn('pencil', false,true, function(btn){
25972                 Roo.log(this);
25973                 this.toggleSourceEdit(btn.pressed);
25974         });
25975         
25976         if (this.editor.btns.length > 0) {
25977             for (var i = 0; i<this.editor.btns.length; i++) {
25978                 children.push(this.editor.btns[i]);
25979             }
25980         }
25981         
25982         /*
25983         var cog = {
25984                 xtype: 'Button',
25985                 size : 'sm',
25986                 xns: Roo.bootstrap,
25987                 glyphicon : 'cog',
25988                 //html : 'submit'
25989                 menu : {
25990                     xtype: 'Menu',
25991                     xns: Roo.bootstrap,
25992                     items:  []
25993                 }
25994         };
25995         
25996         cog.menu.items.push({
25997             xtype :'MenuItem',
25998             xns: Roo.bootstrap,
25999             html : Clean styles,
26000             tagname : f,
26001             listeners : {
26002                 click : function()
26003                 {
26004                     editorcore.insertTag(this.tagname);
26005                     editor.focus();
26006                 }
26007             }
26008             
26009         });
26010        */
26011         
26012          
26013        this.xtype = 'NavSimplebar';
26014         
26015         for(var i=0;i< children.length;i++) {
26016             
26017             this.buttons.add(this.addxtypeChild(children[i]));
26018             
26019         }
26020         
26021         editor.on('editorevent', this.updateToolbar, this);
26022     },
26023     onBtnClick : function(id)
26024     {
26025        this.editorcore.relayCmd(id);
26026        this.editorcore.focus();
26027     },
26028     
26029     /**
26030      * Protected method that will not generally be called directly. It triggers
26031      * a toolbar update by reading the markup state of the current selection in the editor.
26032      */
26033     updateToolbar: function(){
26034
26035         if(!this.editorcore.activated){
26036             this.editor.onFirstFocus(); // is this neeed?
26037             return;
26038         }
26039
26040         var btns = this.buttons; 
26041         var doc = this.editorcore.doc;
26042         btns.get('bold').setActive(doc.queryCommandState('bold'));
26043         btns.get('italic').setActive(doc.queryCommandState('italic'));
26044         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26045         
26046         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26047         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26048         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26049         
26050         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26051         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26052          /*
26053         
26054         var ans = this.editorcore.getAllAncestors();
26055         if (this.formatCombo) {
26056             
26057             
26058             var store = this.formatCombo.store;
26059             this.formatCombo.setValue("");
26060             for (var i =0; i < ans.length;i++) {
26061                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26062                     // select it..
26063                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26064                     break;
26065                 }
26066             }
26067         }
26068         
26069         
26070         
26071         // hides menus... - so this cant be on a menu...
26072         Roo.bootstrap.MenuMgr.hideAll();
26073         */
26074         Roo.bootstrap.MenuMgr.hideAll();
26075         //this.editorsyncValue();
26076     },
26077     onFirstFocus: function() {
26078         this.buttons.each(function(item){
26079            item.enable();
26080         });
26081     },
26082     toggleSourceEdit : function(sourceEditMode){
26083         
26084           
26085         if(sourceEditMode){
26086             Roo.log("disabling buttons");
26087            this.buttons.each( function(item){
26088                 if(item.cmd != 'pencil'){
26089                     item.disable();
26090                 }
26091             });
26092           
26093         }else{
26094             Roo.log("enabling buttons");
26095             if(this.editorcore.initialized){
26096                 this.buttons.each( function(item){
26097                     item.enable();
26098                 });
26099             }
26100             
26101         }
26102         Roo.log("calling toggole on editor");
26103         // tell the editor that it's been pressed..
26104         this.editor.toggleSourceEdit(sourceEditMode);
26105        
26106     }
26107 });
26108
26109
26110
26111
26112  
26113 /*
26114  * - LGPL
26115  */
26116
26117 /**
26118  * @class Roo.bootstrap.Markdown
26119  * @extends Roo.bootstrap.TextArea
26120  * Bootstrap Showdown editable area
26121  * @cfg {string} content
26122  * 
26123  * @constructor
26124  * Create a new Showdown
26125  */
26126
26127 Roo.bootstrap.Markdown = function(config){
26128     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26129    
26130 };
26131
26132 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26133     
26134     editing :false,
26135     
26136     initEvents : function()
26137     {
26138         
26139         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26140         this.markdownEl = this.el.createChild({
26141             cls : 'roo-markdown-area'
26142         });
26143         this.inputEl().addClass('d-none');
26144         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26145         this.markdownEl.on('click', this.toggleTextEdit, this);
26146         this.on('blur', this.toggleTextEdit, this);
26147         this.on('specialkey', this.resizeTextArea, this);
26148     },
26149     
26150     toggleTextEdit : function()
26151     {
26152         var sh = this.markdownEl.getHeight();
26153         this.inputEl().addClass('d-none');
26154         this.markdownEl.addClass('d-none');
26155         if (!this.editing) {
26156             // show editor?
26157             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26158             this.inputEl().removeClass('d-none');
26159             this.inputEl().focus();
26160             this.editing = true;
26161             return;
26162         }
26163         // show showdown...
26164         this.updateMarkdown();
26165         this.markdownEl.removeClass('d-none');
26166         this.editing = false;
26167         return;
26168     },
26169     updateMarkdown : function()
26170     {
26171         if (this.getValue() == '') {
26172             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder);
26173             return;
26174         }
26175         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26176     },
26177     
26178     resizeTextArea: function () {
26179         
26180         var sh = 100;
26181         Roo.log([sh, this.getValue().split("\n").length * 30]);
26182         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26183     },
26184     setValue : function(val)
26185     {
26186         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26187         if (!this.editing) {
26188             this.updateMarkdown();
26189         }
26190         
26191     },
26192     focus : function()
26193     {
26194         if (!this.editing) {
26195             this.toggleTextEdit();
26196         }
26197         
26198     }
26199
26200
26201 });
26202 /**
26203  * @class Roo.bootstrap.Table.AbstractSelectionModel
26204  * @extends Roo.util.Observable
26205  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26206  * implemented by descendant classes.  This class should not be directly instantiated.
26207  * @constructor
26208  */
26209 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26210     this.locked = false;
26211     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26212 };
26213
26214
26215 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26216     /** @ignore Called by the grid automatically. Do not call directly. */
26217     init : function(grid){
26218         this.grid = grid;
26219         this.initEvents();
26220     },
26221
26222     /**
26223      * Locks the selections.
26224      */
26225     lock : function(){
26226         this.locked = true;
26227     },
26228
26229     /**
26230      * Unlocks the selections.
26231      */
26232     unlock : function(){
26233         this.locked = false;
26234     },
26235
26236     /**
26237      * Returns true if the selections are locked.
26238      * @return {Boolean}
26239      */
26240     isLocked : function(){
26241         return this.locked;
26242     },
26243     
26244     
26245     initEvents : function ()
26246     {
26247         
26248     }
26249 });
26250 /**
26251  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26252  * @class Roo.bootstrap.Table.RowSelectionModel
26253  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26254  * It supports multiple selections and keyboard selection/navigation. 
26255  * @constructor
26256  * @param {Object} config
26257  */
26258
26259 Roo.bootstrap.Table.RowSelectionModel = function(config){
26260     Roo.apply(this, config);
26261     this.selections = new Roo.util.MixedCollection(false, function(o){
26262         return o.id;
26263     });
26264
26265     this.last = false;
26266     this.lastActive = false;
26267
26268     this.addEvents({
26269         /**
26270              * @event selectionchange
26271              * Fires when the selection changes
26272              * @param {SelectionModel} this
26273              */
26274             "selectionchange" : true,
26275         /**
26276              * @event afterselectionchange
26277              * Fires after the selection changes (eg. by key press or clicking)
26278              * @param {SelectionModel} this
26279              */
26280             "afterselectionchange" : true,
26281         /**
26282              * @event beforerowselect
26283              * Fires when a row is selected being selected, return false to cancel.
26284              * @param {SelectionModel} this
26285              * @param {Number} rowIndex The selected index
26286              * @param {Boolean} keepExisting False if other selections will be cleared
26287              */
26288             "beforerowselect" : true,
26289         /**
26290              * @event rowselect
26291              * Fires when a row is selected.
26292              * @param {SelectionModel} this
26293              * @param {Number} rowIndex The selected index
26294              * @param {Roo.data.Record} r The record
26295              */
26296             "rowselect" : true,
26297         /**
26298              * @event rowdeselect
26299              * Fires when a row is deselected.
26300              * @param {SelectionModel} this
26301              * @param {Number} rowIndex The selected index
26302              */
26303         "rowdeselect" : true
26304     });
26305     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26306     this.locked = false;
26307  };
26308
26309 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26310     /**
26311      * @cfg {Boolean} singleSelect
26312      * True to allow selection of only one row at a time (defaults to false)
26313      */
26314     singleSelect : false,
26315
26316     // private
26317     initEvents : function()
26318     {
26319
26320         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26321         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26322         //}else{ // allow click to work like normal
26323          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26324         //}
26325         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26326         this.grid.on("rowclick", this.handleMouseDown, this);
26327         
26328         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26329             "up" : function(e){
26330                 if(!e.shiftKey){
26331                     this.selectPrevious(e.shiftKey);
26332                 }else if(this.last !== false && this.lastActive !== false){
26333                     var last = this.last;
26334                     this.selectRange(this.last,  this.lastActive-1);
26335                     this.grid.getView().focusRow(this.lastActive);
26336                     if(last !== false){
26337                         this.last = last;
26338                     }
26339                 }else{
26340                     this.selectFirstRow();
26341                 }
26342                 this.fireEvent("afterselectionchange", this);
26343             },
26344             "down" : function(e){
26345                 if(!e.shiftKey){
26346                     this.selectNext(e.shiftKey);
26347                 }else if(this.last !== false && this.lastActive !== false){
26348                     var last = this.last;
26349                     this.selectRange(this.last,  this.lastActive+1);
26350                     this.grid.getView().focusRow(this.lastActive);
26351                     if(last !== false){
26352                         this.last = last;
26353                     }
26354                 }else{
26355                     this.selectFirstRow();
26356                 }
26357                 this.fireEvent("afterselectionchange", this);
26358             },
26359             scope: this
26360         });
26361         this.grid.store.on('load', function(){
26362             this.selections.clear();
26363         },this);
26364         /*
26365         var view = this.grid.view;
26366         view.on("refresh", this.onRefresh, this);
26367         view.on("rowupdated", this.onRowUpdated, this);
26368         view.on("rowremoved", this.onRemove, this);
26369         */
26370     },
26371
26372     // private
26373     onRefresh : function()
26374     {
26375         var ds = this.grid.store, i, v = this.grid.view;
26376         var s = this.selections;
26377         s.each(function(r){
26378             if((i = ds.indexOfId(r.id)) != -1){
26379                 v.onRowSelect(i);
26380             }else{
26381                 s.remove(r);
26382             }
26383         });
26384     },
26385
26386     // private
26387     onRemove : function(v, index, r){
26388         this.selections.remove(r);
26389     },
26390
26391     // private
26392     onRowUpdated : function(v, index, r){
26393         if(this.isSelected(r)){
26394             v.onRowSelect(index);
26395         }
26396     },
26397
26398     /**
26399      * Select records.
26400      * @param {Array} records The records to select
26401      * @param {Boolean} keepExisting (optional) True to keep existing selections
26402      */
26403     selectRecords : function(records, keepExisting)
26404     {
26405         if(!keepExisting){
26406             this.clearSelections();
26407         }
26408             var ds = this.grid.store;
26409         for(var i = 0, len = records.length; i < len; i++){
26410             this.selectRow(ds.indexOf(records[i]), true);
26411         }
26412     },
26413
26414     /**
26415      * Gets the number of selected rows.
26416      * @return {Number}
26417      */
26418     getCount : function(){
26419         return this.selections.length;
26420     },
26421
26422     /**
26423      * Selects the first row in the grid.
26424      */
26425     selectFirstRow : function(){
26426         this.selectRow(0);
26427     },
26428
26429     /**
26430      * Select the last row.
26431      * @param {Boolean} keepExisting (optional) True to keep existing selections
26432      */
26433     selectLastRow : function(keepExisting){
26434         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26435         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26436     },
26437
26438     /**
26439      * Selects the row immediately following the last selected row.
26440      * @param {Boolean} keepExisting (optional) True to keep existing selections
26441      */
26442     selectNext : function(keepExisting)
26443     {
26444             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26445             this.selectRow(this.last+1, keepExisting);
26446             this.grid.getView().focusRow(this.last);
26447         }
26448     },
26449
26450     /**
26451      * Selects the row that precedes the last selected row.
26452      * @param {Boolean} keepExisting (optional) True to keep existing selections
26453      */
26454     selectPrevious : function(keepExisting){
26455         if(this.last){
26456             this.selectRow(this.last-1, keepExisting);
26457             this.grid.getView().focusRow(this.last);
26458         }
26459     },
26460
26461     /**
26462      * Returns the selected records
26463      * @return {Array} Array of selected records
26464      */
26465     getSelections : function(){
26466         return [].concat(this.selections.items);
26467     },
26468
26469     /**
26470      * Returns the first selected record.
26471      * @return {Record}
26472      */
26473     getSelected : function(){
26474         return this.selections.itemAt(0);
26475     },
26476
26477
26478     /**
26479      * Clears all selections.
26480      */
26481     clearSelections : function(fast)
26482     {
26483         if(this.locked) {
26484             return;
26485         }
26486         if(fast !== true){
26487                 var ds = this.grid.store;
26488             var s = this.selections;
26489             s.each(function(r){
26490                 this.deselectRow(ds.indexOfId(r.id));
26491             }, this);
26492             s.clear();
26493         }else{
26494             this.selections.clear();
26495         }
26496         this.last = false;
26497     },
26498
26499
26500     /**
26501      * Selects all rows.
26502      */
26503     selectAll : function(){
26504         if(this.locked) {
26505             return;
26506         }
26507         this.selections.clear();
26508         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26509             this.selectRow(i, true);
26510         }
26511     },
26512
26513     /**
26514      * Returns True if there is a selection.
26515      * @return {Boolean}
26516      */
26517     hasSelection : function(){
26518         return this.selections.length > 0;
26519     },
26520
26521     /**
26522      * Returns True if the specified row is selected.
26523      * @param {Number/Record} record The record or index of the record to check
26524      * @return {Boolean}
26525      */
26526     isSelected : function(index){
26527             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26528         return (r && this.selections.key(r.id) ? true : false);
26529     },
26530
26531     /**
26532      * Returns True if the specified record id is selected.
26533      * @param {String} id The id of record to check
26534      * @return {Boolean}
26535      */
26536     isIdSelected : function(id){
26537         return (this.selections.key(id) ? true : false);
26538     },
26539
26540
26541     // private
26542     handleMouseDBClick : function(e, t){
26543         
26544     },
26545     // private
26546     handleMouseDown : function(e, t)
26547     {
26548             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26549         if(this.isLocked() || rowIndex < 0 ){
26550             return;
26551         };
26552         if(e.shiftKey && this.last !== false){
26553             var last = this.last;
26554             this.selectRange(last, rowIndex, e.ctrlKey);
26555             this.last = last; // reset the last
26556             t.focus();
26557     
26558         }else{
26559             var isSelected = this.isSelected(rowIndex);
26560             //Roo.log("select row:" + rowIndex);
26561             if(isSelected){
26562                 this.deselectRow(rowIndex);
26563             } else {
26564                         this.selectRow(rowIndex, true);
26565             }
26566     
26567             /*
26568                 if(e.button !== 0 && isSelected){
26569                 alert('rowIndex 2: ' + rowIndex);
26570                     view.focusRow(rowIndex);
26571                 }else if(e.ctrlKey && isSelected){
26572                     this.deselectRow(rowIndex);
26573                 }else if(!isSelected){
26574                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26575                     view.focusRow(rowIndex);
26576                 }
26577             */
26578         }
26579         this.fireEvent("afterselectionchange", this);
26580     },
26581     // private
26582     handleDragableRowClick :  function(grid, rowIndex, e) 
26583     {
26584         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26585             this.selectRow(rowIndex, false);
26586             grid.view.focusRow(rowIndex);
26587              this.fireEvent("afterselectionchange", this);
26588         }
26589     },
26590     
26591     /**
26592      * Selects multiple rows.
26593      * @param {Array} rows Array of the indexes of the row to select
26594      * @param {Boolean} keepExisting (optional) True to keep existing selections
26595      */
26596     selectRows : function(rows, keepExisting){
26597         if(!keepExisting){
26598             this.clearSelections();
26599         }
26600         for(var i = 0, len = rows.length; i < len; i++){
26601             this.selectRow(rows[i], true);
26602         }
26603     },
26604
26605     /**
26606      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26607      * @param {Number} startRow The index of the first row in the range
26608      * @param {Number} endRow The index of the last row in the range
26609      * @param {Boolean} keepExisting (optional) True to retain existing selections
26610      */
26611     selectRange : function(startRow, endRow, keepExisting){
26612         if(this.locked) {
26613             return;
26614         }
26615         if(!keepExisting){
26616             this.clearSelections();
26617         }
26618         if(startRow <= endRow){
26619             for(var i = startRow; i <= endRow; i++){
26620                 this.selectRow(i, true);
26621             }
26622         }else{
26623             for(var i = startRow; i >= endRow; i--){
26624                 this.selectRow(i, true);
26625             }
26626         }
26627     },
26628
26629     /**
26630      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26631      * @param {Number} startRow The index of the first row in the range
26632      * @param {Number} endRow The index of the last row in the range
26633      */
26634     deselectRange : function(startRow, endRow, preventViewNotify){
26635         if(this.locked) {
26636             return;
26637         }
26638         for(var i = startRow; i <= endRow; i++){
26639             this.deselectRow(i, preventViewNotify);
26640         }
26641     },
26642
26643     /**
26644      * Selects a row.
26645      * @param {Number} row The index of the row to select
26646      * @param {Boolean} keepExisting (optional) True to keep existing selections
26647      */
26648     selectRow : function(index, keepExisting, preventViewNotify)
26649     {
26650             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26651             return;
26652         }
26653         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26654             if(!keepExisting || this.singleSelect){
26655                 this.clearSelections();
26656             }
26657             
26658             var r = this.grid.store.getAt(index);
26659             //console.log('selectRow - record id :' + r.id);
26660             
26661             this.selections.add(r);
26662             this.last = this.lastActive = index;
26663             if(!preventViewNotify){
26664                 var proxy = new Roo.Element(
26665                                 this.grid.getRowDom(index)
26666                 );
26667                 proxy.addClass('bg-info info');
26668             }
26669             this.fireEvent("rowselect", this, index, r);
26670             this.fireEvent("selectionchange", this);
26671         }
26672     },
26673
26674     /**
26675      * Deselects a row.
26676      * @param {Number} row The index of the row to deselect
26677      */
26678     deselectRow : function(index, preventViewNotify)
26679     {
26680         if(this.locked) {
26681             return;
26682         }
26683         if(this.last == index){
26684             this.last = false;
26685         }
26686         if(this.lastActive == index){
26687             this.lastActive = false;
26688         }
26689         
26690         var r = this.grid.store.getAt(index);
26691         if (!r) {
26692             return;
26693         }
26694         
26695         this.selections.remove(r);
26696         //.console.log('deselectRow - record id :' + r.id);
26697         if(!preventViewNotify){
26698         
26699             var proxy = new Roo.Element(
26700                 this.grid.getRowDom(index)
26701             );
26702             proxy.removeClass('bg-info info');
26703         }
26704         this.fireEvent("rowdeselect", this, index);
26705         this.fireEvent("selectionchange", this);
26706     },
26707
26708     // private
26709     restoreLast : function(){
26710         if(this._last){
26711             this.last = this._last;
26712         }
26713     },
26714
26715     // private
26716     acceptsNav : function(row, col, cm){
26717         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26718     },
26719
26720     // private
26721     onEditorKey : function(field, e){
26722         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26723         if(k == e.TAB){
26724             e.stopEvent();
26725             ed.completeEdit();
26726             if(e.shiftKey){
26727                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26728             }else{
26729                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26730             }
26731         }else if(k == e.ENTER && !e.ctrlKey){
26732             e.stopEvent();
26733             ed.completeEdit();
26734             if(e.shiftKey){
26735                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26736             }else{
26737                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26738             }
26739         }else if(k == e.ESC){
26740             ed.cancelEdit();
26741         }
26742         if(newCell){
26743             g.startEditing(newCell[0], newCell[1]);
26744         }
26745     }
26746 });
26747 /*
26748  * Based on:
26749  * Ext JS Library 1.1.1
26750  * Copyright(c) 2006-2007, Ext JS, LLC.
26751  *
26752  * Originally Released Under LGPL - original licence link has changed is not relivant.
26753  *
26754  * Fork - LGPL
26755  * <script type="text/javascript">
26756  */
26757  
26758 /**
26759  * @class Roo.bootstrap.PagingToolbar
26760  * @extends Roo.bootstrap.NavSimplebar
26761  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26762  * @constructor
26763  * Create a new PagingToolbar
26764  * @param {Object} config The config object
26765  * @param {Roo.data.Store} store
26766  */
26767 Roo.bootstrap.PagingToolbar = function(config)
26768 {
26769     // old args format still supported... - xtype is prefered..
26770         // created from xtype...
26771     
26772     this.ds = config.dataSource;
26773     
26774     if (config.store && !this.ds) {
26775         this.store= Roo.factory(config.store, Roo.data);
26776         this.ds = this.store;
26777         this.ds.xmodule = this.xmodule || false;
26778     }
26779     
26780     this.toolbarItems = [];
26781     if (config.items) {
26782         this.toolbarItems = config.items;
26783     }
26784     
26785     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26786     
26787     this.cursor = 0;
26788     
26789     if (this.ds) { 
26790         this.bind(this.ds);
26791     }
26792     
26793     if (Roo.bootstrap.version == 4) {
26794         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26795     } else {
26796         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26797     }
26798     
26799 };
26800
26801 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26802     /**
26803      * @cfg {Roo.data.Store} dataSource
26804      * The underlying data store providing the paged data
26805      */
26806     /**
26807      * @cfg {String/HTMLElement/Element} container
26808      * container The id or element that will contain the toolbar
26809      */
26810     /**
26811      * @cfg {Boolean} displayInfo
26812      * True to display the displayMsg (defaults to false)
26813      */
26814     /**
26815      * @cfg {Number} pageSize
26816      * The number of records to display per page (defaults to 20)
26817      */
26818     pageSize: 20,
26819     /**
26820      * @cfg {String} displayMsg
26821      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26822      */
26823     displayMsg : 'Displaying {0} - {1} of {2}',
26824     /**
26825      * @cfg {String} emptyMsg
26826      * The message to display when no records are found (defaults to "No data to display")
26827      */
26828     emptyMsg : 'No data to display',
26829     /**
26830      * Customizable piece of the default paging text (defaults to "Page")
26831      * @type String
26832      */
26833     beforePageText : "Page",
26834     /**
26835      * Customizable piece of the default paging text (defaults to "of %0")
26836      * @type String
26837      */
26838     afterPageText : "of {0}",
26839     /**
26840      * Customizable piece of the default paging text (defaults to "First Page")
26841      * @type String
26842      */
26843     firstText : "First Page",
26844     /**
26845      * Customizable piece of the default paging text (defaults to "Previous Page")
26846      * @type String
26847      */
26848     prevText : "Previous Page",
26849     /**
26850      * Customizable piece of the default paging text (defaults to "Next Page")
26851      * @type String
26852      */
26853     nextText : "Next Page",
26854     /**
26855      * Customizable piece of the default paging text (defaults to "Last Page")
26856      * @type String
26857      */
26858     lastText : "Last Page",
26859     /**
26860      * Customizable piece of the default paging text (defaults to "Refresh")
26861      * @type String
26862      */
26863     refreshText : "Refresh",
26864
26865     buttons : false,
26866     // private
26867     onRender : function(ct, position) 
26868     {
26869         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26870         this.navgroup.parentId = this.id;
26871         this.navgroup.onRender(this.el, null);
26872         // add the buttons to the navgroup
26873         
26874         if(this.displayInfo){
26875             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26876             this.displayEl = this.el.select('.x-paging-info', true).first();
26877 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26878 //            this.displayEl = navel.el.select('span',true).first();
26879         }
26880         
26881         var _this = this;
26882         
26883         if(this.buttons){
26884             Roo.each(_this.buttons, function(e){ // this might need to use render????
26885                Roo.factory(e).render(_this.el);
26886             });
26887         }
26888             
26889         Roo.each(_this.toolbarItems, function(e) {
26890             _this.navgroup.addItem(e);
26891         });
26892         
26893         
26894         this.first = this.navgroup.addItem({
26895             tooltip: this.firstText,
26896             cls: "prev btn-outline-secondary",
26897             html : ' <i class="fa fa-step-backward"></i>',
26898             disabled: true,
26899             preventDefault: true,
26900             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26901         });
26902         
26903         this.prev =  this.navgroup.addItem({
26904             tooltip: this.prevText,
26905             cls: "prev btn-outline-secondary",
26906             html : ' <i class="fa fa-backward"></i>',
26907             disabled: true,
26908             preventDefault: true,
26909             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26910         });
26911     //this.addSeparator();
26912         
26913         
26914         var field = this.navgroup.addItem( {
26915             tagtype : 'span',
26916             cls : 'x-paging-position  btn-outline-secondary',
26917              disabled: true,
26918             html : this.beforePageText  +
26919                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26920                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26921          } ); //?? escaped?
26922         
26923         this.field = field.el.select('input', true).first();
26924         this.field.on("keydown", this.onPagingKeydown, this);
26925         this.field.on("focus", function(){this.dom.select();});
26926     
26927     
26928         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26929         //this.field.setHeight(18);
26930         //this.addSeparator();
26931         this.next = this.navgroup.addItem({
26932             tooltip: this.nextText,
26933             cls: "next btn-outline-secondary",
26934             html : ' <i class="fa fa-forward"></i>',
26935             disabled: true,
26936             preventDefault: true,
26937             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26938         });
26939         this.last = this.navgroup.addItem({
26940             tooltip: this.lastText,
26941             html : ' <i class="fa fa-step-forward"></i>',
26942             cls: "next btn-outline-secondary",
26943             disabled: true,
26944             preventDefault: true,
26945             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26946         });
26947     //this.addSeparator();
26948         this.loading = this.navgroup.addItem({
26949             tooltip: this.refreshText,
26950             cls: "btn-outline-secondary",
26951             html : ' <i class="fa fa-refresh"></i>',
26952             preventDefault: true,
26953             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26954         });
26955         
26956     },
26957
26958     // private
26959     updateInfo : function(){
26960         if(this.displayEl){
26961             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26962             var msg = count == 0 ?
26963                 this.emptyMsg :
26964                 String.format(
26965                     this.displayMsg,
26966                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26967                 );
26968             this.displayEl.update(msg);
26969         }
26970     },
26971
26972     // private
26973     onLoad : function(ds, r, o)
26974     {
26975         this.cursor = o.params.start ? o.params.start : 0;
26976         
26977         var d = this.getPageData(),
26978             ap = d.activePage,
26979             ps = d.pages;
26980         
26981         
26982         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26983         this.field.dom.value = ap;
26984         this.first.setDisabled(ap == 1);
26985         this.prev.setDisabled(ap == 1);
26986         this.next.setDisabled(ap == ps);
26987         this.last.setDisabled(ap == ps);
26988         this.loading.enable();
26989         this.updateInfo();
26990     },
26991
26992     // private
26993     getPageData : function(){
26994         var total = this.ds.getTotalCount();
26995         return {
26996             total : total,
26997             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26998             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26999         };
27000     },
27001
27002     // private
27003     onLoadError : function(){
27004         this.loading.enable();
27005     },
27006
27007     // private
27008     onPagingKeydown : function(e){
27009         var k = e.getKey();
27010         var d = this.getPageData();
27011         if(k == e.RETURN){
27012             var v = this.field.dom.value, pageNum;
27013             if(!v || isNaN(pageNum = parseInt(v, 10))){
27014                 this.field.dom.value = d.activePage;
27015                 return;
27016             }
27017             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27018             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27019             e.stopEvent();
27020         }
27021         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))
27022         {
27023           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27024           this.field.dom.value = pageNum;
27025           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27026           e.stopEvent();
27027         }
27028         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27029         {
27030           var v = this.field.dom.value, pageNum; 
27031           var increment = (e.shiftKey) ? 10 : 1;
27032           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27033                 increment *= -1;
27034           }
27035           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27036             this.field.dom.value = d.activePage;
27037             return;
27038           }
27039           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27040           {
27041             this.field.dom.value = parseInt(v, 10) + increment;
27042             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27043             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27044           }
27045           e.stopEvent();
27046         }
27047     },
27048
27049     // private
27050     beforeLoad : function(){
27051         if(this.loading){
27052             this.loading.disable();
27053         }
27054     },
27055
27056     // private
27057     onClick : function(which){
27058         
27059         var ds = this.ds;
27060         if (!ds) {
27061             return;
27062         }
27063         
27064         switch(which){
27065             case "first":
27066                 ds.load({params:{start: 0, limit: this.pageSize}});
27067             break;
27068             case "prev":
27069                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27070             break;
27071             case "next":
27072                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27073             break;
27074             case "last":
27075                 var total = ds.getTotalCount();
27076                 var extra = total % this.pageSize;
27077                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27078                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27079             break;
27080             case "refresh":
27081                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27082             break;
27083         }
27084     },
27085
27086     /**
27087      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27088      * @param {Roo.data.Store} store The data store to unbind
27089      */
27090     unbind : function(ds){
27091         ds.un("beforeload", this.beforeLoad, this);
27092         ds.un("load", this.onLoad, this);
27093         ds.un("loadexception", this.onLoadError, this);
27094         ds.un("remove", this.updateInfo, this);
27095         ds.un("add", this.updateInfo, this);
27096         this.ds = undefined;
27097     },
27098
27099     /**
27100      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27101      * @param {Roo.data.Store} store The data store to bind
27102      */
27103     bind : function(ds){
27104         ds.on("beforeload", this.beforeLoad, this);
27105         ds.on("load", this.onLoad, this);
27106         ds.on("loadexception", this.onLoadError, this);
27107         ds.on("remove", this.updateInfo, this);
27108         ds.on("add", this.updateInfo, this);
27109         this.ds = ds;
27110     }
27111 });/*
27112  * - LGPL
27113  *
27114  * element
27115  * 
27116  */
27117
27118 /**
27119  * @class Roo.bootstrap.MessageBar
27120  * @extends Roo.bootstrap.Component
27121  * Bootstrap MessageBar class
27122  * @cfg {String} html contents of the MessageBar
27123  * @cfg {String} weight (info | success | warning | danger) default info
27124  * @cfg {String} beforeClass insert the bar before the given class
27125  * @cfg {Boolean} closable (true | false) default false
27126  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27127  * 
27128  * @constructor
27129  * Create a new Element
27130  * @param {Object} config The config object
27131  */
27132
27133 Roo.bootstrap.MessageBar = function(config){
27134     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27135 };
27136
27137 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27138     
27139     html: '',
27140     weight: 'info',
27141     closable: false,
27142     fixed: false,
27143     beforeClass: 'bootstrap-sticky-wrap',
27144     
27145     getAutoCreate : function(){
27146         
27147         var cfg = {
27148             tag: 'div',
27149             cls: 'alert alert-dismissable alert-' + this.weight,
27150             cn: [
27151                 {
27152                     tag: 'span',
27153                     cls: 'message',
27154                     html: this.html || ''
27155                 }
27156             ]
27157         };
27158         
27159         if(this.fixed){
27160             cfg.cls += ' alert-messages-fixed';
27161         }
27162         
27163         if(this.closable){
27164             cfg.cn.push({
27165                 tag: 'button',
27166                 cls: 'close',
27167                 html: 'x'
27168             });
27169         }
27170         
27171         return cfg;
27172     },
27173     
27174     onRender : function(ct, position)
27175     {
27176         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27177         
27178         if(!this.el){
27179             var cfg = Roo.apply({},  this.getAutoCreate());
27180             cfg.id = Roo.id();
27181             
27182             if (this.cls) {
27183                 cfg.cls += ' ' + this.cls;
27184             }
27185             if (this.style) {
27186                 cfg.style = this.style;
27187             }
27188             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27189             
27190             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27191         }
27192         
27193         this.el.select('>button.close').on('click', this.hide, this);
27194         
27195     },
27196     
27197     show : function()
27198     {
27199         if (!this.rendered) {
27200             this.render();
27201         }
27202         
27203         this.el.show();
27204         
27205         this.fireEvent('show', this);
27206         
27207     },
27208     
27209     hide : function()
27210     {
27211         if (!this.rendered) {
27212             this.render();
27213         }
27214         
27215         this.el.hide();
27216         
27217         this.fireEvent('hide', this);
27218     },
27219     
27220     update : function()
27221     {
27222 //        var e = this.el.dom.firstChild;
27223 //        
27224 //        if(this.closable){
27225 //            e = e.nextSibling;
27226 //        }
27227 //        
27228 //        e.data = this.html || '';
27229
27230         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27231     }
27232    
27233 });
27234
27235  
27236
27237      /*
27238  * - LGPL
27239  *
27240  * Graph
27241  * 
27242  */
27243
27244
27245 /**
27246  * @class Roo.bootstrap.Graph
27247  * @extends Roo.bootstrap.Component
27248  * Bootstrap Graph class
27249 > Prameters
27250  -sm {number} sm 4
27251  -md {number} md 5
27252  @cfg {String} graphtype  bar | vbar | pie
27253  @cfg {number} g_x coodinator | centre x (pie)
27254  @cfg {number} g_y coodinator | centre y (pie)
27255  @cfg {number} g_r radius (pie)
27256  @cfg {number} g_height height of the chart (respected by all elements in the set)
27257  @cfg {number} g_width width of the chart (respected by all elements in the set)
27258  @cfg {Object} title The title of the chart
27259     
27260  -{Array}  values
27261  -opts (object) options for the chart 
27262      o {
27263      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27264      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27265      o vgutter (number)
27266      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.
27267      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27268      o to
27269      o stretch (boolean)
27270      o }
27271  -opts (object) options for the pie
27272      o{
27273      o cut
27274      o startAngle (number)
27275      o endAngle (number)
27276      } 
27277  *
27278  * @constructor
27279  * Create a new Input
27280  * @param {Object} config The config object
27281  */
27282
27283 Roo.bootstrap.Graph = function(config){
27284     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27285     
27286     this.addEvents({
27287         // img events
27288         /**
27289          * @event click
27290          * The img click event for the img.
27291          * @param {Roo.EventObject} e
27292          */
27293         "click" : true
27294     });
27295 };
27296
27297 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27298     
27299     sm: 4,
27300     md: 5,
27301     graphtype: 'bar',
27302     g_height: 250,
27303     g_width: 400,
27304     g_x: 50,
27305     g_y: 50,
27306     g_r: 30,
27307     opts:{
27308         //g_colors: this.colors,
27309         g_type: 'soft',
27310         g_gutter: '20%'
27311
27312     },
27313     title : false,
27314
27315     getAutoCreate : function(){
27316         
27317         var cfg = {
27318             tag: 'div',
27319             html : null
27320         };
27321         
27322         
27323         return  cfg;
27324     },
27325
27326     onRender : function(ct,position){
27327         
27328         
27329         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27330         
27331         if (typeof(Raphael) == 'undefined') {
27332             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27333             return;
27334         }
27335         
27336         this.raphael = Raphael(this.el.dom);
27337         
27338                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27339                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27340                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27341                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27342                 /*
27343                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27344                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27345                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27346                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27347                 
27348                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27349                 r.barchart(330, 10, 300, 220, data1);
27350                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27351                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27352                 */
27353                 
27354                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27355                 // r.barchart(30, 30, 560, 250,  xdata, {
27356                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27357                 //     axis : "0 0 1 1",
27358                 //     axisxlabels :  xdata
27359                 //     //yvalues : cols,
27360                    
27361                 // });
27362 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27363 //        
27364 //        this.load(null,xdata,{
27365 //                axis : "0 0 1 1",
27366 //                axisxlabels :  xdata
27367 //                });
27368
27369     },
27370
27371     load : function(graphtype,xdata,opts)
27372     {
27373         this.raphael.clear();
27374         if(!graphtype) {
27375             graphtype = this.graphtype;
27376         }
27377         if(!opts){
27378             opts = this.opts;
27379         }
27380         var r = this.raphael,
27381             fin = function () {
27382                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27383             },
27384             fout = function () {
27385                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27386             },
27387             pfin = function() {
27388                 this.sector.stop();
27389                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27390
27391                 if (this.label) {
27392                     this.label[0].stop();
27393                     this.label[0].attr({ r: 7.5 });
27394                     this.label[1].attr({ "font-weight": 800 });
27395                 }
27396             },
27397             pfout = function() {
27398                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27399
27400                 if (this.label) {
27401                     this.label[0].animate({ r: 5 }, 500, "bounce");
27402                     this.label[1].attr({ "font-weight": 400 });
27403                 }
27404             };
27405
27406         switch(graphtype){
27407             case 'bar':
27408                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27409                 break;
27410             case 'hbar':
27411                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27412                 break;
27413             case 'pie':
27414 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27415 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27416 //            
27417                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27418                 
27419                 break;
27420
27421         }
27422         
27423         if(this.title){
27424             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27425         }
27426         
27427     },
27428     
27429     setTitle: function(o)
27430     {
27431         this.title = o;
27432     },
27433     
27434     initEvents: function() {
27435         
27436         if(!this.href){
27437             this.el.on('click', this.onClick, this);
27438         }
27439     },
27440     
27441     onClick : function(e)
27442     {
27443         Roo.log('img onclick');
27444         this.fireEvent('click', this, e);
27445     }
27446    
27447 });
27448
27449  
27450 /*
27451  * - LGPL
27452  *
27453  * numberBox
27454  * 
27455  */
27456 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27457
27458 /**
27459  * @class Roo.bootstrap.dash.NumberBox
27460  * @extends Roo.bootstrap.Component
27461  * Bootstrap NumberBox class
27462  * @cfg {String} headline Box headline
27463  * @cfg {String} content Box content
27464  * @cfg {String} icon Box icon
27465  * @cfg {String} footer Footer text
27466  * @cfg {String} fhref Footer href
27467  * 
27468  * @constructor
27469  * Create a new NumberBox
27470  * @param {Object} config The config object
27471  */
27472
27473
27474 Roo.bootstrap.dash.NumberBox = function(config){
27475     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27476     
27477 };
27478
27479 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27480     
27481     headline : '',
27482     content : '',
27483     icon : '',
27484     footer : '',
27485     fhref : '',
27486     ficon : '',
27487     
27488     getAutoCreate : function(){
27489         
27490         var cfg = {
27491             tag : 'div',
27492             cls : 'small-box ',
27493             cn : [
27494                 {
27495                     tag : 'div',
27496                     cls : 'inner',
27497                     cn :[
27498                         {
27499                             tag : 'h3',
27500                             cls : 'roo-headline',
27501                             html : this.headline
27502                         },
27503                         {
27504                             tag : 'p',
27505                             cls : 'roo-content',
27506                             html : this.content
27507                         }
27508                     ]
27509                 }
27510             ]
27511         };
27512         
27513         if(this.icon){
27514             cfg.cn.push({
27515                 tag : 'div',
27516                 cls : 'icon',
27517                 cn :[
27518                     {
27519                         tag : 'i',
27520                         cls : 'ion ' + this.icon
27521                     }
27522                 ]
27523             });
27524         }
27525         
27526         if(this.footer){
27527             var footer = {
27528                 tag : 'a',
27529                 cls : 'small-box-footer',
27530                 href : this.fhref || '#',
27531                 html : this.footer
27532             };
27533             
27534             cfg.cn.push(footer);
27535             
27536         }
27537         
27538         return  cfg;
27539     },
27540
27541     onRender : function(ct,position){
27542         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27543
27544
27545        
27546                 
27547     },
27548
27549     setHeadline: function (value)
27550     {
27551         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27552     },
27553     
27554     setFooter: function (value, href)
27555     {
27556         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27557         
27558         if(href){
27559             this.el.select('a.small-box-footer',true).first().attr('href', href);
27560         }
27561         
27562     },
27563
27564     setContent: function (value)
27565     {
27566         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27567     },
27568
27569     initEvents: function() 
27570     {   
27571         
27572     }
27573     
27574 });
27575
27576  
27577 /*
27578  * - LGPL
27579  *
27580  * TabBox
27581  * 
27582  */
27583 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27584
27585 /**
27586  * @class Roo.bootstrap.dash.TabBox
27587  * @extends Roo.bootstrap.Component
27588  * Bootstrap TabBox class
27589  * @cfg {String} title Title of the TabBox
27590  * @cfg {String} icon Icon of the TabBox
27591  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27592  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27593  * 
27594  * @constructor
27595  * Create a new TabBox
27596  * @param {Object} config The config object
27597  */
27598
27599
27600 Roo.bootstrap.dash.TabBox = function(config){
27601     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27602     this.addEvents({
27603         // raw events
27604         /**
27605          * @event addpane
27606          * When a pane is added
27607          * @param {Roo.bootstrap.dash.TabPane} pane
27608          */
27609         "addpane" : true,
27610         /**
27611          * @event activatepane
27612          * When a pane is activated
27613          * @param {Roo.bootstrap.dash.TabPane} pane
27614          */
27615         "activatepane" : true
27616         
27617          
27618     });
27619     
27620     this.panes = [];
27621 };
27622
27623 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27624
27625     title : '',
27626     icon : false,
27627     showtabs : true,
27628     tabScrollable : false,
27629     
27630     getChildContainer : function()
27631     {
27632         return this.el.select('.tab-content', true).first();
27633     },
27634     
27635     getAutoCreate : function(){
27636         
27637         var header = {
27638             tag: 'li',
27639             cls: 'pull-left header',
27640             html: this.title,
27641             cn : []
27642         };
27643         
27644         if(this.icon){
27645             header.cn.push({
27646                 tag: 'i',
27647                 cls: 'fa ' + this.icon
27648             });
27649         }
27650         
27651         var h = {
27652             tag: 'ul',
27653             cls: 'nav nav-tabs pull-right',
27654             cn: [
27655                 header
27656             ]
27657         };
27658         
27659         if(this.tabScrollable){
27660             h = {
27661                 tag: 'div',
27662                 cls: 'tab-header',
27663                 cn: [
27664                     {
27665                         tag: 'ul',
27666                         cls: 'nav nav-tabs pull-right',
27667                         cn: [
27668                             header
27669                         ]
27670                     }
27671                 ]
27672             };
27673         }
27674         
27675         var cfg = {
27676             tag: 'div',
27677             cls: 'nav-tabs-custom',
27678             cn: [
27679                 h,
27680                 {
27681                     tag: 'div',
27682                     cls: 'tab-content no-padding',
27683                     cn: []
27684                 }
27685             ]
27686         };
27687
27688         return  cfg;
27689     },
27690     initEvents : function()
27691     {
27692         //Roo.log('add add pane handler');
27693         this.on('addpane', this.onAddPane, this);
27694     },
27695      /**
27696      * Updates the box title
27697      * @param {String} html to set the title to.
27698      */
27699     setTitle : function(value)
27700     {
27701         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27702     },
27703     onAddPane : function(pane)
27704     {
27705         this.panes.push(pane);
27706         //Roo.log('addpane');
27707         //Roo.log(pane);
27708         // tabs are rendere left to right..
27709         if(!this.showtabs){
27710             return;
27711         }
27712         
27713         var ctr = this.el.select('.nav-tabs', true).first();
27714          
27715          
27716         var existing = ctr.select('.nav-tab',true);
27717         var qty = existing.getCount();;
27718         
27719         
27720         var tab = ctr.createChild({
27721             tag : 'li',
27722             cls : 'nav-tab' + (qty ? '' : ' active'),
27723             cn : [
27724                 {
27725                     tag : 'a',
27726                     href:'#',
27727                     html : pane.title
27728                 }
27729             ]
27730         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27731         pane.tab = tab;
27732         
27733         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27734         if (!qty) {
27735             pane.el.addClass('active');
27736         }
27737         
27738                 
27739     },
27740     onTabClick : function(ev,un,ob,pane)
27741     {
27742         //Roo.log('tab - prev default');
27743         ev.preventDefault();
27744         
27745         
27746         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27747         pane.tab.addClass('active');
27748         //Roo.log(pane.title);
27749         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27750         // technically we should have a deactivate event.. but maybe add later.
27751         // and it should not de-activate the selected tab...
27752         this.fireEvent('activatepane', pane);
27753         pane.el.addClass('active');
27754         pane.fireEvent('activate');
27755         
27756         
27757     },
27758     
27759     getActivePane : function()
27760     {
27761         var r = false;
27762         Roo.each(this.panes, function(p) {
27763             if(p.el.hasClass('active')){
27764                 r = p;
27765                 return false;
27766             }
27767             
27768             return;
27769         });
27770         
27771         return r;
27772     }
27773     
27774     
27775 });
27776
27777  
27778 /*
27779  * - LGPL
27780  *
27781  * Tab pane
27782  * 
27783  */
27784 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27785 /**
27786  * @class Roo.bootstrap.TabPane
27787  * @extends Roo.bootstrap.Component
27788  * Bootstrap TabPane class
27789  * @cfg {Boolean} active (false | true) Default false
27790  * @cfg {String} title title of panel
27791
27792  * 
27793  * @constructor
27794  * Create a new TabPane
27795  * @param {Object} config The config object
27796  */
27797
27798 Roo.bootstrap.dash.TabPane = function(config){
27799     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27800     
27801     this.addEvents({
27802         // raw events
27803         /**
27804          * @event activate
27805          * When a pane is activated
27806          * @param {Roo.bootstrap.dash.TabPane} pane
27807          */
27808         "activate" : true
27809          
27810     });
27811 };
27812
27813 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27814     
27815     active : false,
27816     title : '',
27817     
27818     // the tabBox that this is attached to.
27819     tab : false,
27820      
27821     getAutoCreate : function() 
27822     {
27823         var cfg = {
27824             tag: 'div',
27825             cls: 'tab-pane'
27826         };
27827         
27828         if(this.active){
27829             cfg.cls += ' active';
27830         }
27831         
27832         return cfg;
27833     },
27834     initEvents  : function()
27835     {
27836         //Roo.log('trigger add pane handler');
27837         this.parent().fireEvent('addpane', this)
27838     },
27839     
27840      /**
27841      * Updates the tab title 
27842      * @param {String} html to set the title to.
27843      */
27844     setTitle: function(str)
27845     {
27846         if (!this.tab) {
27847             return;
27848         }
27849         this.title = str;
27850         this.tab.select('a', true).first().dom.innerHTML = str;
27851         
27852     }
27853     
27854     
27855     
27856 });
27857
27858  
27859
27860
27861  /*
27862  * - LGPL
27863  *
27864  * menu
27865  * 
27866  */
27867 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27868
27869 /**
27870  * @class Roo.bootstrap.menu.Menu
27871  * @extends Roo.bootstrap.Component
27872  * Bootstrap Menu class - container for Menu
27873  * @cfg {String} html Text of the menu
27874  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27875  * @cfg {String} icon Font awesome icon
27876  * @cfg {String} pos Menu align to (top | bottom) default bottom
27877  * 
27878  * 
27879  * @constructor
27880  * Create a new Menu
27881  * @param {Object} config The config object
27882  */
27883
27884
27885 Roo.bootstrap.menu.Menu = function(config){
27886     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27887     
27888     this.addEvents({
27889         /**
27890          * @event beforeshow
27891          * Fires before this menu is displayed
27892          * @param {Roo.bootstrap.menu.Menu} this
27893          */
27894         beforeshow : true,
27895         /**
27896          * @event beforehide
27897          * Fires before this menu is hidden
27898          * @param {Roo.bootstrap.menu.Menu} this
27899          */
27900         beforehide : true,
27901         /**
27902          * @event show
27903          * Fires after this menu is displayed
27904          * @param {Roo.bootstrap.menu.Menu} this
27905          */
27906         show : true,
27907         /**
27908          * @event hide
27909          * Fires after this menu is hidden
27910          * @param {Roo.bootstrap.menu.Menu} this
27911          */
27912         hide : true,
27913         /**
27914          * @event click
27915          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27916          * @param {Roo.bootstrap.menu.Menu} this
27917          * @param {Roo.EventObject} e
27918          */
27919         click : true
27920     });
27921     
27922 };
27923
27924 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27925     
27926     submenu : false,
27927     html : '',
27928     weight : 'default',
27929     icon : false,
27930     pos : 'bottom',
27931     
27932     
27933     getChildContainer : function() {
27934         if(this.isSubMenu){
27935             return this.el;
27936         }
27937         
27938         return this.el.select('ul.dropdown-menu', true).first();  
27939     },
27940     
27941     getAutoCreate : function()
27942     {
27943         var text = [
27944             {
27945                 tag : 'span',
27946                 cls : 'roo-menu-text',
27947                 html : this.html
27948             }
27949         ];
27950         
27951         if(this.icon){
27952             text.unshift({
27953                 tag : 'i',
27954                 cls : 'fa ' + this.icon
27955             })
27956         }
27957         
27958         
27959         var cfg = {
27960             tag : 'div',
27961             cls : 'btn-group',
27962             cn : [
27963                 {
27964                     tag : 'button',
27965                     cls : 'dropdown-button btn btn-' + this.weight,
27966                     cn : text
27967                 },
27968                 {
27969                     tag : 'button',
27970                     cls : 'dropdown-toggle btn btn-' + this.weight,
27971                     cn : [
27972                         {
27973                             tag : 'span',
27974                             cls : 'caret'
27975                         }
27976                     ]
27977                 },
27978                 {
27979                     tag : 'ul',
27980                     cls : 'dropdown-menu'
27981                 }
27982             ]
27983             
27984         };
27985         
27986         if(this.pos == 'top'){
27987             cfg.cls += ' dropup';
27988         }
27989         
27990         if(this.isSubMenu){
27991             cfg = {
27992                 tag : 'ul',
27993                 cls : 'dropdown-menu'
27994             }
27995         }
27996         
27997         return cfg;
27998     },
27999     
28000     onRender : function(ct, position)
28001     {
28002         this.isSubMenu = ct.hasClass('dropdown-submenu');
28003         
28004         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28005     },
28006     
28007     initEvents : function() 
28008     {
28009         if(this.isSubMenu){
28010             return;
28011         }
28012         
28013         this.hidden = true;
28014         
28015         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28016         this.triggerEl.on('click', this.onTriggerPress, this);
28017         
28018         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28019         this.buttonEl.on('click', this.onClick, this);
28020         
28021     },
28022     
28023     list : function()
28024     {
28025         if(this.isSubMenu){
28026             return this.el;
28027         }
28028         
28029         return this.el.select('ul.dropdown-menu', true).first();
28030     },
28031     
28032     onClick : function(e)
28033     {
28034         this.fireEvent("click", this, e);
28035     },
28036     
28037     onTriggerPress  : function(e)
28038     {   
28039         if (this.isVisible()) {
28040             this.hide();
28041         } else {
28042             this.show();
28043         }
28044     },
28045     
28046     isVisible : function(){
28047         return !this.hidden;
28048     },
28049     
28050     show : function()
28051     {
28052         this.fireEvent("beforeshow", this);
28053         
28054         this.hidden = false;
28055         this.el.addClass('open');
28056         
28057         Roo.get(document).on("mouseup", this.onMouseUp, this);
28058         
28059         this.fireEvent("show", this);
28060         
28061         
28062     },
28063     
28064     hide : function()
28065     {
28066         this.fireEvent("beforehide", this);
28067         
28068         this.hidden = true;
28069         this.el.removeClass('open');
28070         
28071         Roo.get(document).un("mouseup", this.onMouseUp);
28072         
28073         this.fireEvent("hide", this);
28074     },
28075     
28076     onMouseUp : function()
28077     {
28078         this.hide();
28079     }
28080     
28081 });
28082
28083  
28084  /*
28085  * - LGPL
28086  *
28087  * menu item
28088  * 
28089  */
28090 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28091
28092 /**
28093  * @class Roo.bootstrap.menu.Item
28094  * @extends Roo.bootstrap.Component
28095  * Bootstrap MenuItem class
28096  * @cfg {Boolean} submenu (true | false) default false
28097  * @cfg {String} html text of the item
28098  * @cfg {String} href the link
28099  * @cfg {Boolean} disable (true | false) default false
28100  * @cfg {Boolean} preventDefault (true | false) default true
28101  * @cfg {String} icon Font awesome icon
28102  * @cfg {String} pos Submenu align to (left | right) default right 
28103  * 
28104  * 
28105  * @constructor
28106  * Create a new Item
28107  * @param {Object} config The config object
28108  */
28109
28110
28111 Roo.bootstrap.menu.Item = function(config){
28112     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28113     this.addEvents({
28114         /**
28115          * @event mouseover
28116          * Fires when the mouse is hovering over this menu
28117          * @param {Roo.bootstrap.menu.Item} this
28118          * @param {Roo.EventObject} e
28119          */
28120         mouseover : true,
28121         /**
28122          * @event mouseout
28123          * Fires when the mouse exits this menu
28124          * @param {Roo.bootstrap.menu.Item} this
28125          * @param {Roo.EventObject} e
28126          */
28127         mouseout : true,
28128         // raw events
28129         /**
28130          * @event click
28131          * The raw click event for the entire grid.
28132          * @param {Roo.EventObject} e
28133          */
28134         click : true
28135     });
28136 };
28137
28138 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28139     
28140     submenu : false,
28141     href : '',
28142     html : '',
28143     preventDefault: true,
28144     disable : false,
28145     icon : false,
28146     pos : 'right',
28147     
28148     getAutoCreate : function()
28149     {
28150         var text = [
28151             {
28152                 tag : 'span',
28153                 cls : 'roo-menu-item-text',
28154                 html : this.html
28155             }
28156         ];
28157         
28158         if(this.icon){
28159             text.unshift({
28160                 tag : 'i',
28161                 cls : 'fa ' + this.icon
28162             })
28163         }
28164         
28165         var cfg = {
28166             tag : 'li',
28167             cn : [
28168                 {
28169                     tag : 'a',
28170                     href : this.href || '#',
28171                     cn : text
28172                 }
28173             ]
28174         };
28175         
28176         if(this.disable){
28177             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28178         }
28179         
28180         if(this.submenu){
28181             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28182             
28183             if(this.pos == 'left'){
28184                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28185             }
28186         }
28187         
28188         return cfg;
28189     },
28190     
28191     initEvents : function() 
28192     {
28193         this.el.on('mouseover', this.onMouseOver, this);
28194         this.el.on('mouseout', this.onMouseOut, this);
28195         
28196         this.el.select('a', true).first().on('click', this.onClick, this);
28197         
28198     },
28199     
28200     onClick : function(e)
28201     {
28202         if(this.preventDefault){
28203             e.preventDefault();
28204         }
28205         
28206         this.fireEvent("click", this, e);
28207     },
28208     
28209     onMouseOver : function(e)
28210     {
28211         if(this.submenu && this.pos == 'left'){
28212             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28213         }
28214         
28215         this.fireEvent("mouseover", this, e);
28216     },
28217     
28218     onMouseOut : function(e)
28219     {
28220         this.fireEvent("mouseout", this, e);
28221     }
28222 });
28223
28224  
28225
28226  /*
28227  * - LGPL
28228  *
28229  * menu separator
28230  * 
28231  */
28232 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28233
28234 /**
28235  * @class Roo.bootstrap.menu.Separator
28236  * @extends Roo.bootstrap.Component
28237  * Bootstrap Separator class
28238  * 
28239  * @constructor
28240  * Create a new Separator
28241  * @param {Object} config The config object
28242  */
28243
28244
28245 Roo.bootstrap.menu.Separator = function(config){
28246     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28247 };
28248
28249 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28250     
28251     getAutoCreate : function(){
28252         var cfg = {
28253             tag : 'li',
28254             cls: 'divider'
28255         };
28256         
28257         return cfg;
28258     }
28259    
28260 });
28261
28262  
28263
28264  /*
28265  * - LGPL
28266  *
28267  * Tooltip
28268  * 
28269  */
28270
28271 /**
28272  * @class Roo.bootstrap.Tooltip
28273  * Bootstrap Tooltip class
28274  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28275  * to determine which dom element triggers the tooltip.
28276  * 
28277  * It needs to add support for additional attributes like tooltip-position
28278  * 
28279  * @constructor
28280  * Create a new Toolti
28281  * @param {Object} config The config object
28282  */
28283
28284 Roo.bootstrap.Tooltip = function(config){
28285     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28286     
28287     this.alignment = Roo.bootstrap.Tooltip.alignment;
28288     
28289     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28290         this.alignment = config.alignment;
28291     }
28292     
28293 };
28294
28295 Roo.apply(Roo.bootstrap.Tooltip, {
28296     /**
28297      * @function init initialize tooltip monitoring.
28298      * @static
28299      */
28300     currentEl : false,
28301     currentTip : false,
28302     currentRegion : false,
28303     
28304     //  init : delay?
28305     
28306     init : function()
28307     {
28308         Roo.get(document).on('mouseover', this.enter ,this);
28309         Roo.get(document).on('mouseout', this.leave, this);
28310          
28311         
28312         this.currentTip = new Roo.bootstrap.Tooltip();
28313     },
28314     
28315     enter : function(ev)
28316     {
28317         var dom = ev.getTarget();
28318         
28319         //Roo.log(['enter',dom]);
28320         var el = Roo.fly(dom);
28321         if (this.currentEl) {
28322             //Roo.log(dom);
28323             //Roo.log(this.currentEl);
28324             //Roo.log(this.currentEl.contains(dom));
28325             if (this.currentEl == el) {
28326                 return;
28327             }
28328             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28329                 return;
28330             }
28331
28332         }
28333         
28334         if (this.currentTip.el) {
28335             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28336         }    
28337         //Roo.log(ev);
28338         
28339         if(!el || el.dom == document){
28340             return;
28341         }
28342         
28343         var bindEl = el;
28344         
28345         // you can not look for children, as if el is the body.. then everythign is the child..
28346         if (!el.attr('tooltip')) { //
28347             if (!el.select("[tooltip]").elements.length) {
28348                 return;
28349             }
28350             // is the mouse over this child...?
28351             bindEl = el.select("[tooltip]").first();
28352             var xy = ev.getXY();
28353             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28354                 //Roo.log("not in region.");
28355                 return;
28356             }
28357             //Roo.log("child element over..");
28358             
28359         }
28360         this.currentEl = bindEl;
28361         this.currentTip.bind(bindEl);
28362         this.currentRegion = Roo.lib.Region.getRegion(dom);
28363         this.currentTip.enter();
28364         
28365     },
28366     leave : function(ev)
28367     {
28368         var dom = ev.getTarget();
28369         //Roo.log(['leave',dom]);
28370         if (!this.currentEl) {
28371             return;
28372         }
28373         
28374         
28375         if (dom != this.currentEl.dom) {
28376             return;
28377         }
28378         var xy = ev.getXY();
28379         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28380             return;
28381         }
28382         // only activate leave if mouse cursor is outside... bounding box..
28383         
28384         
28385         
28386         
28387         if (this.currentTip) {
28388             this.currentTip.leave();
28389         }
28390         //Roo.log('clear currentEl');
28391         this.currentEl = false;
28392         
28393         
28394     },
28395     alignment : {
28396         'left' : ['r-l', [-2,0], 'right'],
28397         'right' : ['l-r', [2,0], 'left'],
28398         'bottom' : ['t-b', [0,2], 'top'],
28399         'top' : [ 'b-t', [0,-2], 'bottom']
28400     }
28401     
28402 });
28403
28404
28405 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28406     
28407     
28408     bindEl : false,
28409     
28410     delay : null, // can be { show : 300 , hide: 500}
28411     
28412     timeout : null,
28413     
28414     hoverState : null, //???
28415     
28416     placement : 'bottom', 
28417     
28418     alignment : false,
28419     
28420     getAutoCreate : function(){
28421     
28422         var cfg = {
28423            cls : 'tooltip',   
28424            role : 'tooltip',
28425            cn : [
28426                 {
28427                     cls : 'tooltip-arrow arrow'
28428                 },
28429                 {
28430                     cls : 'tooltip-inner'
28431                 }
28432            ]
28433         };
28434         
28435         return cfg;
28436     },
28437     bind : function(el)
28438     {
28439         this.bindEl = el;
28440     },
28441     
28442     initEvents : function()
28443     {
28444         this.arrowEl = this.el.select('.arrow', true).first();
28445         this.innerEl = this.el.select('.tooltip-inner', true).first();
28446     },
28447     
28448     enter : function () {
28449        
28450         if (this.timeout != null) {
28451             clearTimeout(this.timeout);
28452         }
28453         
28454         this.hoverState = 'in';
28455          //Roo.log("enter - show");
28456         if (!this.delay || !this.delay.show) {
28457             this.show();
28458             return;
28459         }
28460         var _t = this;
28461         this.timeout = setTimeout(function () {
28462             if (_t.hoverState == 'in') {
28463                 _t.show();
28464             }
28465         }, this.delay.show);
28466     },
28467     leave : function()
28468     {
28469         clearTimeout(this.timeout);
28470     
28471         this.hoverState = 'out';
28472          if (!this.delay || !this.delay.hide) {
28473             this.hide();
28474             return;
28475         }
28476        
28477         var _t = this;
28478         this.timeout = setTimeout(function () {
28479             //Roo.log("leave - timeout");
28480             
28481             if (_t.hoverState == 'out') {
28482                 _t.hide();
28483                 Roo.bootstrap.Tooltip.currentEl = false;
28484             }
28485         }, delay);
28486     },
28487     
28488     show : function (msg)
28489     {
28490         if (!this.el) {
28491             this.render(document.body);
28492         }
28493         // set content.
28494         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28495         
28496         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28497         
28498         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28499         
28500         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28501                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28502         
28503         var placement = typeof this.placement == 'function' ?
28504             this.placement.call(this, this.el, on_el) :
28505             this.placement;
28506             
28507         var autoToken = /\s?auto?\s?/i;
28508         var autoPlace = autoToken.test(placement);
28509         if (autoPlace) {
28510             placement = placement.replace(autoToken, '') || 'top';
28511         }
28512         
28513         //this.el.detach()
28514         //this.el.setXY([0,0]);
28515         this.el.show();
28516         //this.el.dom.style.display='block';
28517         
28518         //this.el.appendTo(on_el);
28519         
28520         var p = this.getPosition();
28521         var box = this.el.getBox();
28522         
28523         if (autoPlace) {
28524             // fixme..
28525         }
28526         
28527         var align = this.alignment[placement];
28528         
28529         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28530         
28531         if(placement == 'top' || placement == 'bottom'){
28532             if(xy[0] < 0){
28533                 placement = 'right';
28534             }
28535             
28536             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28537                 placement = 'left';
28538             }
28539             
28540             var scroll = Roo.select('body', true).first().getScroll();
28541             
28542             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28543                 placement = 'top';
28544             }
28545             
28546             align = this.alignment[placement];
28547             
28548             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28549             
28550         }
28551         
28552         this.el.alignTo(this.bindEl, align[0],align[1]);
28553         //var arrow = this.el.select('.arrow',true).first();
28554         //arrow.set(align[2], 
28555         
28556         this.el.addClass(placement);
28557         this.el.addClass("bs-tooltip-"+ placement);
28558         
28559         this.el.addClass('in fade show');
28560         
28561         this.hoverState = null;
28562         
28563         if (this.el.hasClass('fade')) {
28564             // fade it?
28565         }
28566         
28567         
28568         
28569         
28570         
28571     },
28572     hide : function()
28573     {
28574          
28575         if (!this.el) {
28576             return;
28577         }
28578         //this.el.setXY([0,0]);
28579         this.el.removeClass(['show', 'in']);
28580         //this.el.hide();
28581         
28582     }
28583     
28584 });
28585  
28586
28587  /*
28588  * - LGPL
28589  *
28590  * Location Picker
28591  * 
28592  */
28593
28594 /**
28595  * @class Roo.bootstrap.LocationPicker
28596  * @extends Roo.bootstrap.Component
28597  * Bootstrap LocationPicker class
28598  * @cfg {Number} latitude Position when init default 0
28599  * @cfg {Number} longitude Position when init default 0
28600  * @cfg {Number} zoom default 15
28601  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28602  * @cfg {Boolean} mapTypeControl default false
28603  * @cfg {Boolean} disableDoubleClickZoom default false
28604  * @cfg {Boolean} scrollwheel default true
28605  * @cfg {Boolean} streetViewControl default false
28606  * @cfg {Number} radius default 0
28607  * @cfg {String} locationName
28608  * @cfg {Boolean} draggable default true
28609  * @cfg {Boolean} enableAutocomplete default false
28610  * @cfg {Boolean} enableReverseGeocode default true
28611  * @cfg {String} markerTitle
28612  * 
28613  * @constructor
28614  * Create a new LocationPicker
28615  * @param {Object} config The config object
28616  */
28617
28618
28619 Roo.bootstrap.LocationPicker = function(config){
28620     
28621     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28622     
28623     this.addEvents({
28624         /**
28625          * @event initial
28626          * Fires when the picker initialized.
28627          * @param {Roo.bootstrap.LocationPicker} this
28628          * @param {Google Location} location
28629          */
28630         initial : true,
28631         /**
28632          * @event positionchanged
28633          * Fires when the picker position changed.
28634          * @param {Roo.bootstrap.LocationPicker} this
28635          * @param {Google Location} location
28636          */
28637         positionchanged : true,
28638         /**
28639          * @event resize
28640          * Fires when the map resize.
28641          * @param {Roo.bootstrap.LocationPicker} this
28642          */
28643         resize : true,
28644         /**
28645          * @event show
28646          * Fires when the map show.
28647          * @param {Roo.bootstrap.LocationPicker} this
28648          */
28649         show : true,
28650         /**
28651          * @event hide
28652          * Fires when the map hide.
28653          * @param {Roo.bootstrap.LocationPicker} this
28654          */
28655         hide : true,
28656         /**
28657          * @event mapClick
28658          * Fires when click the map.
28659          * @param {Roo.bootstrap.LocationPicker} this
28660          * @param {Map event} e
28661          */
28662         mapClick : true,
28663         /**
28664          * @event mapRightClick
28665          * Fires when right click the map.
28666          * @param {Roo.bootstrap.LocationPicker} this
28667          * @param {Map event} e
28668          */
28669         mapRightClick : true,
28670         /**
28671          * @event markerClick
28672          * Fires when click the marker.
28673          * @param {Roo.bootstrap.LocationPicker} this
28674          * @param {Map event} e
28675          */
28676         markerClick : true,
28677         /**
28678          * @event markerRightClick
28679          * Fires when right click the marker.
28680          * @param {Roo.bootstrap.LocationPicker} this
28681          * @param {Map event} e
28682          */
28683         markerRightClick : true,
28684         /**
28685          * @event OverlayViewDraw
28686          * Fires when OverlayView Draw
28687          * @param {Roo.bootstrap.LocationPicker} this
28688          */
28689         OverlayViewDraw : true,
28690         /**
28691          * @event OverlayViewOnAdd
28692          * Fires when OverlayView Draw
28693          * @param {Roo.bootstrap.LocationPicker} this
28694          */
28695         OverlayViewOnAdd : true,
28696         /**
28697          * @event OverlayViewOnRemove
28698          * Fires when OverlayView Draw
28699          * @param {Roo.bootstrap.LocationPicker} this
28700          */
28701         OverlayViewOnRemove : true,
28702         /**
28703          * @event OverlayViewShow
28704          * Fires when OverlayView Draw
28705          * @param {Roo.bootstrap.LocationPicker} this
28706          * @param {Pixel} cpx
28707          */
28708         OverlayViewShow : true,
28709         /**
28710          * @event OverlayViewHide
28711          * Fires when OverlayView Draw
28712          * @param {Roo.bootstrap.LocationPicker} this
28713          */
28714         OverlayViewHide : true,
28715         /**
28716          * @event loadexception
28717          * Fires when load google lib failed.
28718          * @param {Roo.bootstrap.LocationPicker} this
28719          */
28720         loadexception : true
28721     });
28722         
28723 };
28724
28725 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28726     
28727     gMapContext: false,
28728     
28729     latitude: 0,
28730     longitude: 0,
28731     zoom: 15,
28732     mapTypeId: false,
28733     mapTypeControl: false,
28734     disableDoubleClickZoom: false,
28735     scrollwheel: true,
28736     streetViewControl: false,
28737     radius: 0,
28738     locationName: '',
28739     draggable: true,
28740     enableAutocomplete: false,
28741     enableReverseGeocode: true,
28742     markerTitle: '',
28743     
28744     getAutoCreate: function()
28745     {
28746
28747         var cfg = {
28748             tag: 'div',
28749             cls: 'roo-location-picker'
28750         };
28751         
28752         return cfg
28753     },
28754     
28755     initEvents: function(ct, position)
28756     {       
28757         if(!this.el.getWidth() || this.isApplied()){
28758             return;
28759         }
28760         
28761         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28762         
28763         this.initial();
28764     },
28765     
28766     initial: function()
28767     {
28768         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28769             this.fireEvent('loadexception', this);
28770             return;
28771         }
28772         
28773         if(!this.mapTypeId){
28774             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28775         }
28776         
28777         this.gMapContext = this.GMapContext();
28778         
28779         this.initOverlayView();
28780         
28781         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28782         
28783         var _this = this;
28784                 
28785         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28786             _this.setPosition(_this.gMapContext.marker.position);
28787         });
28788         
28789         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28790             _this.fireEvent('mapClick', this, event);
28791             
28792         });
28793
28794         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28795             _this.fireEvent('mapRightClick', this, event);
28796             
28797         });
28798         
28799         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28800             _this.fireEvent('markerClick', this, event);
28801             
28802         });
28803
28804         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28805             _this.fireEvent('markerRightClick', this, event);
28806             
28807         });
28808         
28809         this.setPosition(this.gMapContext.location);
28810         
28811         this.fireEvent('initial', this, this.gMapContext.location);
28812     },
28813     
28814     initOverlayView: function()
28815     {
28816         var _this = this;
28817         
28818         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28819             
28820             draw: function()
28821             {
28822                 _this.fireEvent('OverlayViewDraw', _this);
28823             },
28824             
28825             onAdd: function()
28826             {
28827                 _this.fireEvent('OverlayViewOnAdd', _this);
28828             },
28829             
28830             onRemove: function()
28831             {
28832                 _this.fireEvent('OverlayViewOnRemove', _this);
28833             },
28834             
28835             show: function(cpx)
28836             {
28837                 _this.fireEvent('OverlayViewShow', _this, cpx);
28838             },
28839             
28840             hide: function()
28841             {
28842                 _this.fireEvent('OverlayViewHide', _this);
28843             }
28844             
28845         });
28846     },
28847     
28848     fromLatLngToContainerPixel: function(event)
28849     {
28850         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28851     },
28852     
28853     isApplied: function() 
28854     {
28855         return this.getGmapContext() == false ? false : true;
28856     },
28857     
28858     getGmapContext: function() 
28859     {
28860         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28861     },
28862     
28863     GMapContext: function() 
28864     {
28865         var position = new google.maps.LatLng(this.latitude, this.longitude);
28866         
28867         var _map = new google.maps.Map(this.el.dom, {
28868             center: position,
28869             zoom: this.zoom,
28870             mapTypeId: this.mapTypeId,
28871             mapTypeControl: this.mapTypeControl,
28872             disableDoubleClickZoom: this.disableDoubleClickZoom,
28873             scrollwheel: this.scrollwheel,
28874             streetViewControl: this.streetViewControl,
28875             locationName: this.locationName,
28876             draggable: this.draggable,
28877             enableAutocomplete: this.enableAutocomplete,
28878             enableReverseGeocode: this.enableReverseGeocode
28879         });
28880         
28881         var _marker = new google.maps.Marker({
28882             position: position,
28883             map: _map,
28884             title: this.markerTitle,
28885             draggable: this.draggable
28886         });
28887         
28888         return {
28889             map: _map,
28890             marker: _marker,
28891             circle: null,
28892             location: position,
28893             radius: this.radius,
28894             locationName: this.locationName,
28895             addressComponents: {
28896                 formatted_address: null,
28897                 addressLine1: null,
28898                 addressLine2: null,
28899                 streetName: null,
28900                 streetNumber: null,
28901                 city: null,
28902                 district: null,
28903                 state: null,
28904                 stateOrProvince: null
28905             },
28906             settings: this,
28907             domContainer: this.el.dom,
28908             geodecoder: new google.maps.Geocoder()
28909         };
28910     },
28911     
28912     drawCircle: function(center, radius, options) 
28913     {
28914         if (this.gMapContext.circle != null) {
28915             this.gMapContext.circle.setMap(null);
28916         }
28917         if (radius > 0) {
28918             radius *= 1;
28919             options = Roo.apply({}, options, {
28920                 strokeColor: "#0000FF",
28921                 strokeOpacity: .35,
28922                 strokeWeight: 2,
28923                 fillColor: "#0000FF",
28924                 fillOpacity: .2
28925             });
28926             
28927             options.map = this.gMapContext.map;
28928             options.radius = radius;
28929             options.center = center;
28930             this.gMapContext.circle = new google.maps.Circle(options);
28931             return this.gMapContext.circle;
28932         }
28933         
28934         return null;
28935     },
28936     
28937     setPosition: function(location) 
28938     {
28939         this.gMapContext.location = location;
28940         this.gMapContext.marker.setPosition(location);
28941         this.gMapContext.map.panTo(location);
28942         this.drawCircle(location, this.gMapContext.radius, {});
28943         
28944         var _this = this;
28945         
28946         if (this.gMapContext.settings.enableReverseGeocode) {
28947             this.gMapContext.geodecoder.geocode({
28948                 latLng: this.gMapContext.location
28949             }, function(results, status) {
28950                 
28951                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28952                     _this.gMapContext.locationName = results[0].formatted_address;
28953                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28954                     
28955                     _this.fireEvent('positionchanged', this, location);
28956                 }
28957             });
28958             
28959             return;
28960         }
28961         
28962         this.fireEvent('positionchanged', this, location);
28963     },
28964     
28965     resize: function()
28966     {
28967         google.maps.event.trigger(this.gMapContext.map, "resize");
28968         
28969         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28970         
28971         this.fireEvent('resize', this);
28972     },
28973     
28974     setPositionByLatLng: function(latitude, longitude)
28975     {
28976         this.setPosition(new google.maps.LatLng(latitude, longitude));
28977     },
28978     
28979     getCurrentPosition: function() 
28980     {
28981         return {
28982             latitude: this.gMapContext.location.lat(),
28983             longitude: this.gMapContext.location.lng()
28984         };
28985     },
28986     
28987     getAddressName: function() 
28988     {
28989         return this.gMapContext.locationName;
28990     },
28991     
28992     getAddressComponents: function() 
28993     {
28994         return this.gMapContext.addressComponents;
28995     },
28996     
28997     address_component_from_google_geocode: function(address_components) 
28998     {
28999         var result = {};
29000         
29001         for (var i = 0; i < address_components.length; i++) {
29002             var component = address_components[i];
29003             if (component.types.indexOf("postal_code") >= 0) {
29004                 result.postalCode = component.short_name;
29005             } else if (component.types.indexOf("street_number") >= 0) {
29006                 result.streetNumber = component.short_name;
29007             } else if (component.types.indexOf("route") >= 0) {
29008                 result.streetName = component.short_name;
29009             } else if (component.types.indexOf("neighborhood") >= 0) {
29010                 result.city = component.short_name;
29011             } else if (component.types.indexOf("locality") >= 0) {
29012                 result.city = component.short_name;
29013             } else if (component.types.indexOf("sublocality") >= 0) {
29014                 result.district = component.short_name;
29015             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29016                 result.stateOrProvince = component.short_name;
29017             } else if (component.types.indexOf("country") >= 0) {
29018                 result.country = component.short_name;
29019             }
29020         }
29021         
29022         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29023         result.addressLine2 = "";
29024         return result;
29025     },
29026     
29027     setZoomLevel: function(zoom)
29028     {
29029         this.gMapContext.map.setZoom(zoom);
29030     },
29031     
29032     show: function()
29033     {
29034         if(!this.el){
29035             return;
29036         }
29037         
29038         this.el.show();
29039         
29040         this.resize();
29041         
29042         this.fireEvent('show', this);
29043     },
29044     
29045     hide: function()
29046     {
29047         if(!this.el){
29048             return;
29049         }
29050         
29051         this.el.hide();
29052         
29053         this.fireEvent('hide', this);
29054     }
29055     
29056 });
29057
29058 Roo.apply(Roo.bootstrap.LocationPicker, {
29059     
29060     OverlayView : function(map, options)
29061     {
29062         options = options || {};
29063         
29064         this.setMap(map);
29065     }
29066     
29067     
29068 });/**
29069  * @class Roo.bootstrap.Alert
29070  * @extends Roo.bootstrap.Component
29071  * Bootstrap Alert class - shows an alert area box
29072  * eg
29073  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29074   Enter a valid email address
29075 </div>
29076  * @licence LGPL
29077  * @cfg {String} title The title of alert
29078  * @cfg {String} html The content of alert
29079  * @cfg {String} weight (  success | info | warning | danger )
29080  * @cfg {String} faicon font-awesomeicon
29081  * 
29082  * @constructor
29083  * Create a new alert
29084  * @param {Object} config The config object
29085  */
29086
29087
29088 Roo.bootstrap.Alert = function(config){
29089     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29090     
29091 };
29092
29093 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29094     
29095     title: '',
29096     html: '',
29097     weight: false,
29098     faicon: false,
29099     
29100     getAutoCreate : function()
29101     {
29102         
29103         var cfg = {
29104             tag : 'div',
29105             cls : 'alert',
29106             cn : [
29107                 {
29108                     tag : 'i',
29109                     cls : 'roo-alert-icon'
29110                     
29111                 },
29112                 {
29113                     tag : 'b',
29114                     cls : 'roo-alert-title',
29115                     html : this.title
29116                 },
29117                 {
29118                     tag : 'span',
29119                     cls : 'roo-alert-text',
29120                     html : this.html
29121                 }
29122             ]
29123         };
29124         
29125         if(this.faicon){
29126             cfg.cn[0].cls += ' fa ' + this.faicon;
29127         }
29128         
29129         if(this.weight){
29130             cfg.cls += ' alert-' + this.weight;
29131         }
29132         
29133         return cfg;
29134     },
29135     
29136     initEvents: function() 
29137     {
29138         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29139     },
29140     
29141     setTitle : function(str)
29142     {
29143         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29144     },
29145     
29146     setText : function(str)
29147     {
29148         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29149     },
29150     
29151     setWeight : function(weight)
29152     {
29153         if(this.weight){
29154             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29155         }
29156         
29157         this.weight = weight;
29158         
29159         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29160     },
29161     
29162     setIcon : function(icon)
29163     {
29164         if(this.faicon){
29165             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29166         }
29167         
29168         this.faicon = icon;
29169         
29170         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29171     },
29172     
29173     hide: function() 
29174     {
29175         this.el.hide();   
29176     },
29177     
29178     show: function() 
29179     {  
29180         this.el.show();   
29181     }
29182     
29183 });
29184
29185  
29186 /*
29187 * Licence: LGPL
29188 */
29189
29190 /**
29191  * @class Roo.bootstrap.UploadCropbox
29192  * @extends Roo.bootstrap.Component
29193  * Bootstrap UploadCropbox class
29194  * @cfg {String} emptyText show when image has been loaded
29195  * @cfg {String} rotateNotify show when image too small to rotate
29196  * @cfg {Number} errorTimeout default 3000
29197  * @cfg {Number} minWidth default 300
29198  * @cfg {Number} minHeight default 300
29199  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29200  * @cfg {Boolean} isDocument (true|false) default false
29201  * @cfg {String} url action url
29202  * @cfg {String} paramName default 'imageUpload'
29203  * @cfg {String} method default POST
29204  * @cfg {Boolean} loadMask (true|false) default true
29205  * @cfg {Boolean} loadingText default 'Loading...'
29206  * 
29207  * @constructor
29208  * Create a new UploadCropbox
29209  * @param {Object} config The config object
29210  */
29211
29212 Roo.bootstrap.UploadCropbox = function(config){
29213     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29214     
29215     this.addEvents({
29216         /**
29217          * @event beforeselectfile
29218          * Fire before select file
29219          * @param {Roo.bootstrap.UploadCropbox} this
29220          */
29221         "beforeselectfile" : true,
29222         /**
29223          * @event initial
29224          * Fire after initEvent
29225          * @param {Roo.bootstrap.UploadCropbox} this
29226          */
29227         "initial" : true,
29228         /**
29229          * @event crop
29230          * Fire after initEvent
29231          * @param {Roo.bootstrap.UploadCropbox} this
29232          * @param {String} data
29233          */
29234         "crop" : true,
29235         /**
29236          * @event prepare
29237          * Fire when preparing the file data
29238          * @param {Roo.bootstrap.UploadCropbox} this
29239          * @param {Object} file
29240          */
29241         "prepare" : true,
29242         /**
29243          * @event exception
29244          * Fire when get exception
29245          * @param {Roo.bootstrap.UploadCropbox} this
29246          * @param {XMLHttpRequest} xhr
29247          */
29248         "exception" : true,
29249         /**
29250          * @event beforeloadcanvas
29251          * Fire before load the canvas
29252          * @param {Roo.bootstrap.UploadCropbox} this
29253          * @param {String} src
29254          */
29255         "beforeloadcanvas" : true,
29256         /**
29257          * @event trash
29258          * Fire when trash image
29259          * @param {Roo.bootstrap.UploadCropbox} this
29260          */
29261         "trash" : true,
29262         /**
29263          * @event download
29264          * Fire when download the image
29265          * @param {Roo.bootstrap.UploadCropbox} this
29266          */
29267         "download" : true,
29268         /**
29269          * @event footerbuttonclick
29270          * Fire when footerbuttonclick
29271          * @param {Roo.bootstrap.UploadCropbox} this
29272          * @param {String} type
29273          */
29274         "footerbuttonclick" : true,
29275         /**
29276          * @event resize
29277          * Fire when resize
29278          * @param {Roo.bootstrap.UploadCropbox} this
29279          */
29280         "resize" : true,
29281         /**
29282          * @event rotate
29283          * Fire when rotate the image
29284          * @param {Roo.bootstrap.UploadCropbox} this
29285          * @param {String} pos
29286          */
29287         "rotate" : true,
29288         /**
29289          * @event inspect
29290          * Fire when inspect the file
29291          * @param {Roo.bootstrap.UploadCropbox} this
29292          * @param {Object} file
29293          */
29294         "inspect" : true,
29295         /**
29296          * @event upload
29297          * Fire when xhr upload the file
29298          * @param {Roo.bootstrap.UploadCropbox} this
29299          * @param {Object} data
29300          */
29301         "upload" : true,
29302         /**
29303          * @event arrange
29304          * Fire when arrange the file data
29305          * @param {Roo.bootstrap.UploadCropbox} this
29306          * @param {Object} formData
29307          */
29308         "arrange" : true
29309     });
29310     
29311     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29312 };
29313
29314 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29315     
29316     emptyText : 'Click to upload image',
29317     rotateNotify : 'Image is too small to rotate',
29318     errorTimeout : 3000,
29319     scale : 0,
29320     baseScale : 1,
29321     rotate : 0,
29322     dragable : false,
29323     pinching : false,
29324     mouseX : 0,
29325     mouseY : 0,
29326     cropData : false,
29327     minWidth : 300,
29328     minHeight : 300,
29329     file : false,
29330     exif : {},
29331     baseRotate : 1,
29332     cropType : 'image/jpeg',
29333     buttons : false,
29334     canvasLoaded : false,
29335     isDocument : false,
29336     method : 'POST',
29337     paramName : 'imageUpload',
29338     loadMask : true,
29339     loadingText : 'Loading...',
29340     maskEl : false,
29341     
29342     getAutoCreate : function()
29343     {
29344         var cfg = {
29345             tag : 'div',
29346             cls : 'roo-upload-cropbox',
29347             cn : [
29348                 {
29349                     tag : 'input',
29350                     cls : 'roo-upload-cropbox-selector',
29351                     type : 'file'
29352                 },
29353                 {
29354                     tag : 'div',
29355                     cls : 'roo-upload-cropbox-body',
29356                     style : 'cursor:pointer',
29357                     cn : [
29358                         {
29359                             tag : 'div',
29360                             cls : 'roo-upload-cropbox-preview'
29361                         },
29362                         {
29363                             tag : 'div',
29364                             cls : 'roo-upload-cropbox-thumb'
29365                         },
29366                         {
29367                             tag : 'div',
29368                             cls : 'roo-upload-cropbox-empty-notify',
29369                             html : this.emptyText
29370                         },
29371                         {
29372                             tag : 'div',
29373                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29374                             html : this.rotateNotify
29375                         }
29376                     ]
29377                 },
29378                 {
29379                     tag : 'div',
29380                     cls : 'roo-upload-cropbox-footer',
29381                     cn : {
29382                         tag : 'div',
29383                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29384                         cn : []
29385                     }
29386                 }
29387             ]
29388         };
29389         
29390         return cfg;
29391     },
29392     
29393     onRender : function(ct, position)
29394     {
29395         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29396         
29397         if (this.buttons.length) {
29398             
29399             Roo.each(this.buttons, function(bb) {
29400                 
29401                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29402                 
29403                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29404                 
29405             }, this);
29406         }
29407         
29408         if(this.loadMask){
29409             this.maskEl = this.el;
29410         }
29411     },
29412     
29413     initEvents : function()
29414     {
29415         this.urlAPI = (window.createObjectURL && window) || 
29416                                 (window.URL && URL.revokeObjectURL && URL) || 
29417                                 (window.webkitURL && webkitURL);
29418                         
29419         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29420         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29421         
29422         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29423         this.selectorEl.hide();
29424         
29425         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29426         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29427         
29428         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29429         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29430         this.thumbEl.hide();
29431         
29432         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29433         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29434         
29435         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29436         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29437         this.errorEl.hide();
29438         
29439         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29440         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29441         this.footerEl.hide();
29442         
29443         this.setThumbBoxSize();
29444         
29445         this.bind();
29446         
29447         this.resize();
29448         
29449         this.fireEvent('initial', this);
29450     },
29451
29452     bind : function()
29453     {
29454         var _this = this;
29455         
29456         window.addEventListener("resize", function() { _this.resize(); } );
29457         
29458         this.bodyEl.on('click', this.beforeSelectFile, this);
29459         
29460         if(Roo.isTouch){
29461             this.bodyEl.on('touchstart', this.onTouchStart, this);
29462             this.bodyEl.on('touchmove', this.onTouchMove, this);
29463             this.bodyEl.on('touchend', this.onTouchEnd, this);
29464         }
29465         
29466         if(!Roo.isTouch){
29467             this.bodyEl.on('mousedown', this.onMouseDown, this);
29468             this.bodyEl.on('mousemove', this.onMouseMove, this);
29469             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29470             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29471             Roo.get(document).on('mouseup', this.onMouseUp, this);
29472         }
29473         
29474         this.selectorEl.on('change', this.onFileSelected, this);
29475     },
29476     
29477     reset : function()
29478     {    
29479         this.scale = 0;
29480         this.baseScale = 1;
29481         this.rotate = 0;
29482         this.baseRotate = 1;
29483         this.dragable = false;
29484         this.pinching = false;
29485         this.mouseX = 0;
29486         this.mouseY = 0;
29487         this.cropData = false;
29488         this.notifyEl.dom.innerHTML = this.emptyText;
29489         
29490         this.selectorEl.dom.value = '';
29491         
29492     },
29493     
29494     resize : function()
29495     {
29496         if(this.fireEvent('resize', this) != false){
29497             this.setThumbBoxPosition();
29498             this.setCanvasPosition();
29499         }
29500     },
29501     
29502     onFooterButtonClick : function(e, el, o, type)
29503     {
29504         switch (type) {
29505             case 'rotate-left' :
29506                 this.onRotateLeft(e);
29507                 break;
29508             case 'rotate-right' :
29509                 this.onRotateRight(e);
29510                 break;
29511             case 'picture' :
29512                 this.beforeSelectFile(e);
29513                 break;
29514             case 'trash' :
29515                 this.trash(e);
29516                 break;
29517             case 'crop' :
29518                 this.crop(e);
29519                 break;
29520             case 'download' :
29521                 this.download(e);
29522                 break;
29523             default :
29524                 break;
29525         }
29526         
29527         this.fireEvent('footerbuttonclick', this, type);
29528     },
29529     
29530     beforeSelectFile : function(e)
29531     {
29532         e.preventDefault();
29533         
29534         if(this.fireEvent('beforeselectfile', this) != false){
29535             this.selectorEl.dom.click();
29536         }
29537     },
29538     
29539     onFileSelected : function(e)
29540     {
29541         e.preventDefault();
29542         
29543         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29544             return;
29545         }
29546         
29547         var file = this.selectorEl.dom.files[0];
29548         
29549         if(this.fireEvent('inspect', this, file) != false){
29550             this.prepare(file);
29551         }
29552         
29553     },
29554     
29555     trash : function(e)
29556     {
29557         this.fireEvent('trash', this);
29558     },
29559     
29560     download : function(e)
29561     {
29562         this.fireEvent('download', this);
29563     },
29564     
29565     loadCanvas : function(src)
29566     {   
29567         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29568             
29569             this.reset();
29570             
29571             this.imageEl = document.createElement('img');
29572             
29573             var _this = this;
29574             
29575             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29576             
29577             this.imageEl.src = src;
29578         }
29579     },
29580     
29581     onLoadCanvas : function()
29582     {   
29583         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29584         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29585         
29586         this.bodyEl.un('click', this.beforeSelectFile, this);
29587         
29588         this.notifyEl.hide();
29589         this.thumbEl.show();
29590         this.footerEl.show();
29591         
29592         this.baseRotateLevel();
29593         
29594         if(this.isDocument){
29595             this.setThumbBoxSize();
29596         }
29597         
29598         this.setThumbBoxPosition();
29599         
29600         this.baseScaleLevel();
29601         
29602         this.draw();
29603         
29604         this.resize();
29605         
29606         this.canvasLoaded = true;
29607         
29608         if(this.loadMask){
29609             this.maskEl.unmask();
29610         }
29611         
29612     },
29613     
29614     setCanvasPosition : function()
29615     {   
29616         if(!this.canvasEl){
29617             return;
29618         }
29619         
29620         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29621         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29622         
29623         this.previewEl.setLeft(pw);
29624         this.previewEl.setTop(ph);
29625         
29626     },
29627     
29628     onMouseDown : function(e)
29629     {   
29630         e.stopEvent();
29631         
29632         this.dragable = true;
29633         this.pinching = false;
29634         
29635         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29636             this.dragable = false;
29637             return;
29638         }
29639         
29640         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29641         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29642         
29643     },
29644     
29645     onMouseMove : function(e)
29646     {   
29647         e.stopEvent();
29648         
29649         if(!this.canvasLoaded){
29650             return;
29651         }
29652         
29653         if (!this.dragable){
29654             return;
29655         }
29656         
29657         var minX = Math.ceil(this.thumbEl.getLeft(true));
29658         var minY = Math.ceil(this.thumbEl.getTop(true));
29659         
29660         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29661         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29662         
29663         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29664         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29665         
29666         x = x - this.mouseX;
29667         y = y - this.mouseY;
29668         
29669         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29670         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29671         
29672         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29673         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29674         
29675         this.previewEl.setLeft(bgX);
29676         this.previewEl.setTop(bgY);
29677         
29678         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29679         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29680     },
29681     
29682     onMouseUp : function(e)
29683     {   
29684         e.stopEvent();
29685         
29686         this.dragable = false;
29687     },
29688     
29689     onMouseWheel : function(e)
29690     {   
29691         e.stopEvent();
29692         
29693         this.startScale = this.scale;
29694         
29695         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29696         
29697         if(!this.zoomable()){
29698             this.scale = this.startScale;
29699             return;
29700         }
29701         
29702         this.draw();
29703         
29704         return;
29705     },
29706     
29707     zoomable : function()
29708     {
29709         var minScale = this.thumbEl.getWidth() / this.minWidth;
29710         
29711         if(this.minWidth < this.minHeight){
29712             minScale = this.thumbEl.getHeight() / this.minHeight;
29713         }
29714         
29715         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29716         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29717         
29718         if(
29719                 this.isDocument &&
29720                 (this.rotate == 0 || this.rotate == 180) && 
29721                 (
29722                     width > this.imageEl.OriginWidth || 
29723                     height > this.imageEl.OriginHeight ||
29724                     (width < this.minWidth && height < this.minHeight)
29725                 )
29726         ){
29727             return false;
29728         }
29729         
29730         if(
29731                 this.isDocument &&
29732                 (this.rotate == 90 || this.rotate == 270) && 
29733                 (
29734                     width > this.imageEl.OriginWidth || 
29735                     height > this.imageEl.OriginHeight ||
29736                     (width < this.minHeight && height < this.minWidth)
29737                 )
29738         ){
29739             return false;
29740         }
29741         
29742         if(
29743                 !this.isDocument &&
29744                 (this.rotate == 0 || this.rotate == 180) && 
29745                 (
29746                     width < this.minWidth || 
29747                     width > this.imageEl.OriginWidth || 
29748                     height < this.minHeight || 
29749                     height > this.imageEl.OriginHeight
29750                 )
29751         ){
29752             return false;
29753         }
29754         
29755         if(
29756                 !this.isDocument &&
29757                 (this.rotate == 90 || this.rotate == 270) && 
29758                 (
29759                     width < this.minHeight || 
29760                     width > this.imageEl.OriginWidth || 
29761                     height < this.minWidth || 
29762                     height > this.imageEl.OriginHeight
29763                 )
29764         ){
29765             return false;
29766         }
29767         
29768         return true;
29769         
29770     },
29771     
29772     onRotateLeft : function(e)
29773     {   
29774         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29775             
29776             var minScale = this.thumbEl.getWidth() / this.minWidth;
29777             
29778             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29779             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29780             
29781             this.startScale = this.scale;
29782             
29783             while (this.getScaleLevel() < minScale){
29784             
29785                 this.scale = this.scale + 1;
29786                 
29787                 if(!this.zoomable()){
29788                     break;
29789                 }
29790                 
29791                 if(
29792                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29793                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29794                 ){
29795                     continue;
29796                 }
29797                 
29798                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29799
29800                 this.draw();
29801                 
29802                 return;
29803             }
29804             
29805             this.scale = this.startScale;
29806             
29807             this.onRotateFail();
29808             
29809             return false;
29810         }
29811         
29812         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29813
29814         if(this.isDocument){
29815             this.setThumbBoxSize();
29816             this.setThumbBoxPosition();
29817             this.setCanvasPosition();
29818         }
29819         
29820         this.draw();
29821         
29822         this.fireEvent('rotate', this, 'left');
29823         
29824     },
29825     
29826     onRotateRight : function(e)
29827     {
29828         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29829             
29830             var minScale = this.thumbEl.getWidth() / this.minWidth;
29831         
29832             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29833             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29834             
29835             this.startScale = this.scale;
29836             
29837             while (this.getScaleLevel() < minScale){
29838             
29839                 this.scale = this.scale + 1;
29840                 
29841                 if(!this.zoomable()){
29842                     break;
29843                 }
29844                 
29845                 if(
29846                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29847                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29848                 ){
29849                     continue;
29850                 }
29851                 
29852                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29853
29854                 this.draw();
29855                 
29856                 return;
29857             }
29858             
29859             this.scale = this.startScale;
29860             
29861             this.onRotateFail();
29862             
29863             return false;
29864         }
29865         
29866         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29867
29868         if(this.isDocument){
29869             this.setThumbBoxSize();
29870             this.setThumbBoxPosition();
29871             this.setCanvasPosition();
29872         }
29873         
29874         this.draw();
29875         
29876         this.fireEvent('rotate', this, 'right');
29877     },
29878     
29879     onRotateFail : function()
29880     {
29881         this.errorEl.show(true);
29882         
29883         var _this = this;
29884         
29885         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29886     },
29887     
29888     draw : function()
29889     {
29890         this.previewEl.dom.innerHTML = '';
29891         
29892         var canvasEl = document.createElement("canvas");
29893         
29894         var contextEl = canvasEl.getContext("2d");
29895         
29896         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29897         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29898         var center = this.imageEl.OriginWidth / 2;
29899         
29900         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29901             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29902             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29903             center = this.imageEl.OriginHeight / 2;
29904         }
29905         
29906         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29907         
29908         contextEl.translate(center, center);
29909         contextEl.rotate(this.rotate * Math.PI / 180);
29910
29911         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29912         
29913         this.canvasEl = document.createElement("canvas");
29914         
29915         this.contextEl = this.canvasEl.getContext("2d");
29916         
29917         switch (this.rotate) {
29918             case 0 :
29919                 
29920                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29921                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29922                 
29923                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29924                 
29925                 break;
29926             case 90 : 
29927                 
29928                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29929                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29930                 
29931                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29932                     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);
29933                     break;
29934                 }
29935                 
29936                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29937                 
29938                 break;
29939             case 180 :
29940                 
29941                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29942                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29943                 
29944                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29945                     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);
29946                     break;
29947                 }
29948                 
29949                 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);
29950                 
29951                 break;
29952             case 270 :
29953                 
29954                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29955                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29956         
29957                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29958                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29959                     break;
29960                 }
29961                 
29962                 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);
29963                 
29964                 break;
29965             default : 
29966                 break;
29967         }
29968         
29969         this.previewEl.appendChild(this.canvasEl);
29970         
29971         this.setCanvasPosition();
29972     },
29973     
29974     crop : function()
29975     {
29976         if(!this.canvasLoaded){
29977             return;
29978         }
29979         
29980         var imageCanvas = document.createElement("canvas");
29981         
29982         var imageContext = imageCanvas.getContext("2d");
29983         
29984         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29985         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29986         
29987         var center = imageCanvas.width / 2;
29988         
29989         imageContext.translate(center, center);
29990         
29991         imageContext.rotate(this.rotate * Math.PI / 180);
29992         
29993         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29994         
29995         var canvas = document.createElement("canvas");
29996         
29997         var context = canvas.getContext("2d");
29998                 
29999         canvas.width = this.minWidth;
30000         canvas.height = this.minHeight;
30001
30002         switch (this.rotate) {
30003             case 0 :
30004                 
30005                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30006                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30007                 
30008                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30009                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30010                 
30011                 var targetWidth = this.minWidth - 2 * x;
30012                 var targetHeight = this.minHeight - 2 * y;
30013                 
30014                 var scale = 1;
30015                 
30016                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30017                     scale = targetWidth / width;
30018                 }
30019                 
30020                 if(x > 0 && y == 0){
30021                     scale = targetHeight / height;
30022                 }
30023                 
30024                 if(x > 0 && y > 0){
30025                     scale = targetWidth / width;
30026                     
30027                     if(width < height){
30028                         scale = targetHeight / height;
30029                     }
30030                 }
30031                 
30032                 context.scale(scale, scale);
30033                 
30034                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30035                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30036
30037                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30038                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30039
30040                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30041                 
30042                 break;
30043             case 90 : 
30044                 
30045                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30046                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30047                 
30048                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30049                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30050                 
30051                 var targetWidth = this.minWidth - 2 * x;
30052                 var targetHeight = this.minHeight - 2 * y;
30053                 
30054                 var scale = 1;
30055                 
30056                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30057                     scale = targetWidth / width;
30058                 }
30059                 
30060                 if(x > 0 && y == 0){
30061                     scale = targetHeight / height;
30062                 }
30063                 
30064                 if(x > 0 && y > 0){
30065                     scale = targetWidth / width;
30066                     
30067                     if(width < height){
30068                         scale = targetHeight / height;
30069                     }
30070                 }
30071                 
30072                 context.scale(scale, scale);
30073                 
30074                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30075                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30076
30077                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30078                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30079                 
30080                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30081                 
30082                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30083                 
30084                 break;
30085             case 180 :
30086                 
30087                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30088                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30089                 
30090                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30091                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30092                 
30093                 var targetWidth = this.minWidth - 2 * x;
30094                 var targetHeight = this.minHeight - 2 * y;
30095                 
30096                 var scale = 1;
30097                 
30098                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30099                     scale = targetWidth / width;
30100                 }
30101                 
30102                 if(x > 0 && y == 0){
30103                     scale = targetHeight / height;
30104                 }
30105                 
30106                 if(x > 0 && y > 0){
30107                     scale = targetWidth / width;
30108                     
30109                     if(width < height){
30110                         scale = targetHeight / height;
30111                     }
30112                 }
30113                 
30114                 context.scale(scale, scale);
30115                 
30116                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30117                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30118
30119                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30120                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30121
30122                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30123                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30124                 
30125                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30126                 
30127                 break;
30128             case 270 :
30129                 
30130                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30131                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30132                 
30133                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30134                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30135                 
30136                 var targetWidth = this.minWidth - 2 * x;
30137                 var targetHeight = this.minHeight - 2 * y;
30138                 
30139                 var scale = 1;
30140                 
30141                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30142                     scale = targetWidth / width;
30143                 }
30144                 
30145                 if(x > 0 && y == 0){
30146                     scale = targetHeight / height;
30147                 }
30148                 
30149                 if(x > 0 && y > 0){
30150                     scale = targetWidth / width;
30151                     
30152                     if(width < height){
30153                         scale = targetHeight / height;
30154                     }
30155                 }
30156                 
30157                 context.scale(scale, scale);
30158                 
30159                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30160                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30161
30162                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30163                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30164                 
30165                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30166                 
30167                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30168                 
30169                 break;
30170             default : 
30171                 break;
30172         }
30173         
30174         this.cropData = canvas.toDataURL(this.cropType);
30175         
30176         if(this.fireEvent('crop', this, this.cropData) !== false){
30177             this.process(this.file, this.cropData);
30178         }
30179         
30180         return;
30181         
30182     },
30183     
30184     setThumbBoxSize : function()
30185     {
30186         var width, height;
30187         
30188         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30189             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30190             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30191             
30192             this.minWidth = width;
30193             this.minHeight = height;
30194             
30195             if(this.rotate == 90 || this.rotate == 270){
30196                 this.minWidth = height;
30197                 this.minHeight = width;
30198             }
30199         }
30200         
30201         height = 300;
30202         width = Math.ceil(this.minWidth * height / this.minHeight);
30203         
30204         if(this.minWidth > this.minHeight){
30205             width = 300;
30206             height = Math.ceil(this.minHeight * width / this.minWidth);
30207         }
30208         
30209         this.thumbEl.setStyle({
30210             width : width + 'px',
30211             height : height + 'px'
30212         });
30213
30214         return;
30215             
30216     },
30217     
30218     setThumbBoxPosition : function()
30219     {
30220         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30221         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30222         
30223         this.thumbEl.setLeft(x);
30224         this.thumbEl.setTop(y);
30225         
30226     },
30227     
30228     baseRotateLevel : function()
30229     {
30230         this.baseRotate = 1;
30231         
30232         if(
30233                 typeof(this.exif) != 'undefined' &&
30234                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30235                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30236         ){
30237             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30238         }
30239         
30240         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30241         
30242     },
30243     
30244     baseScaleLevel : function()
30245     {
30246         var width, height;
30247         
30248         if(this.isDocument){
30249             
30250             if(this.baseRotate == 6 || this.baseRotate == 8){
30251             
30252                 height = this.thumbEl.getHeight();
30253                 this.baseScale = height / this.imageEl.OriginWidth;
30254
30255                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30256                     width = this.thumbEl.getWidth();
30257                     this.baseScale = width / this.imageEl.OriginHeight;
30258                 }
30259
30260                 return;
30261             }
30262
30263             height = this.thumbEl.getHeight();
30264             this.baseScale = height / this.imageEl.OriginHeight;
30265
30266             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30267                 width = this.thumbEl.getWidth();
30268                 this.baseScale = width / this.imageEl.OriginWidth;
30269             }
30270
30271             return;
30272         }
30273         
30274         if(this.baseRotate == 6 || this.baseRotate == 8){
30275             
30276             width = this.thumbEl.getHeight();
30277             this.baseScale = width / this.imageEl.OriginHeight;
30278             
30279             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30280                 height = this.thumbEl.getWidth();
30281                 this.baseScale = height / this.imageEl.OriginHeight;
30282             }
30283             
30284             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30285                 height = this.thumbEl.getWidth();
30286                 this.baseScale = height / this.imageEl.OriginHeight;
30287                 
30288                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30289                     width = this.thumbEl.getHeight();
30290                     this.baseScale = width / this.imageEl.OriginWidth;
30291                 }
30292             }
30293             
30294             return;
30295         }
30296         
30297         width = this.thumbEl.getWidth();
30298         this.baseScale = width / this.imageEl.OriginWidth;
30299         
30300         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30301             height = this.thumbEl.getHeight();
30302             this.baseScale = height / this.imageEl.OriginHeight;
30303         }
30304         
30305         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30306             
30307             height = this.thumbEl.getHeight();
30308             this.baseScale = height / this.imageEl.OriginHeight;
30309             
30310             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30311                 width = this.thumbEl.getWidth();
30312                 this.baseScale = width / this.imageEl.OriginWidth;
30313             }
30314             
30315         }
30316         
30317         return;
30318     },
30319     
30320     getScaleLevel : function()
30321     {
30322         return this.baseScale * Math.pow(1.1, this.scale);
30323     },
30324     
30325     onTouchStart : function(e)
30326     {
30327         if(!this.canvasLoaded){
30328             this.beforeSelectFile(e);
30329             return;
30330         }
30331         
30332         var touches = e.browserEvent.touches;
30333         
30334         if(!touches){
30335             return;
30336         }
30337         
30338         if(touches.length == 1){
30339             this.onMouseDown(e);
30340             return;
30341         }
30342         
30343         if(touches.length != 2){
30344             return;
30345         }
30346         
30347         var coords = [];
30348         
30349         for(var i = 0, finger; finger = touches[i]; i++){
30350             coords.push(finger.pageX, finger.pageY);
30351         }
30352         
30353         var x = Math.pow(coords[0] - coords[2], 2);
30354         var y = Math.pow(coords[1] - coords[3], 2);
30355         
30356         this.startDistance = Math.sqrt(x + y);
30357         
30358         this.startScale = this.scale;
30359         
30360         this.pinching = true;
30361         this.dragable = false;
30362         
30363     },
30364     
30365     onTouchMove : function(e)
30366     {
30367         if(!this.pinching && !this.dragable){
30368             return;
30369         }
30370         
30371         var touches = e.browserEvent.touches;
30372         
30373         if(!touches){
30374             return;
30375         }
30376         
30377         if(this.dragable){
30378             this.onMouseMove(e);
30379             return;
30380         }
30381         
30382         var coords = [];
30383         
30384         for(var i = 0, finger; finger = touches[i]; i++){
30385             coords.push(finger.pageX, finger.pageY);
30386         }
30387         
30388         var x = Math.pow(coords[0] - coords[2], 2);
30389         var y = Math.pow(coords[1] - coords[3], 2);
30390         
30391         this.endDistance = Math.sqrt(x + y);
30392         
30393         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30394         
30395         if(!this.zoomable()){
30396             this.scale = this.startScale;
30397             return;
30398         }
30399         
30400         this.draw();
30401         
30402     },
30403     
30404     onTouchEnd : function(e)
30405     {
30406         this.pinching = false;
30407         this.dragable = false;
30408         
30409     },
30410     
30411     process : function(file, crop)
30412     {
30413         if(this.loadMask){
30414             this.maskEl.mask(this.loadingText);
30415         }
30416         
30417         this.xhr = new XMLHttpRequest();
30418         
30419         file.xhr = this.xhr;
30420
30421         this.xhr.open(this.method, this.url, true);
30422         
30423         var headers = {
30424             "Accept": "application/json",
30425             "Cache-Control": "no-cache",
30426             "X-Requested-With": "XMLHttpRequest"
30427         };
30428         
30429         for (var headerName in headers) {
30430             var headerValue = headers[headerName];
30431             if (headerValue) {
30432                 this.xhr.setRequestHeader(headerName, headerValue);
30433             }
30434         }
30435         
30436         var _this = this;
30437         
30438         this.xhr.onload = function()
30439         {
30440             _this.xhrOnLoad(_this.xhr);
30441         }
30442         
30443         this.xhr.onerror = function()
30444         {
30445             _this.xhrOnError(_this.xhr);
30446         }
30447         
30448         var formData = new FormData();
30449
30450         formData.append('returnHTML', 'NO');
30451         
30452         if(crop){
30453             formData.append('crop', crop);
30454         }
30455         
30456         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30457             formData.append(this.paramName, file, file.name);
30458         }
30459         
30460         if(typeof(file.filename) != 'undefined'){
30461             formData.append('filename', file.filename);
30462         }
30463         
30464         if(typeof(file.mimetype) != 'undefined'){
30465             formData.append('mimetype', file.mimetype);
30466         }
30467         
30468         if(this.fireEvent('arrange', this, formData) != false){
30469             this.xhr.send(formData);
30470         };
30471     },
30472     
30473     xhrOnLoad : function(xhr)
30474     {
30475         if(this.loadMask){
30476             this.maskEl.unmask();
30477         }
30478         
30479         if (xhr.readyState !== 4) {
30480             this.fireEvent('exception', this, xhr);
30481             return;
30482         }
30483
30484         var response = Roo.decode(xhr.responseText);
30485         
30486         if(!response.success){
30487             this.fireEvent('exception', this, xhr);
30488             return;
30489         }
30490         
30491         var response = Roo.decode(xhr.responseText);
30492         
30493         this.fireEvent('upload', this, response);
30494         
30495     },
30496     
30497     xhrOnError : function()
30498     {
30499         if(this.loadMask){
30500             this.maskEl.unmask();
30501         }
30502         
30503         Roo.log('xhr on error');
30504         
30505         var response = Roo.decode(xhr.responseText);
30506           
30507         Roo.log(response);
30508         
30509     },
30510     
30511     prepare : function(file)
30512     {   
30513         if(this.loadMask){
30514             this.maskEl.mask(this.loadingText);
30515         }
30516         
30517         this.file = false;
30518         this.exif = {};
30519         
30520         if(typeof(file) === 'string'){
30521             this.loadCanvas(file);
30522             return;
30523         }
30524         
30525         if(!file || !this.urlAPI){
30526             return;
30527         }
30528         
30529         this.file = file;
30530         this.cropType = file.type;
30531         
30532         var _this = this;
30533         
30534         if(this.fireEvent('prepare', this, this.file) != false){
30535             
30536             var reader = new FileReader();
30537             
30538             reader.onload = function (e) {
30539                 if (e.target.error) {
30540                     Roo.log(e.target.error);
30541                     return;
30542                 }
30543                 
30544                 var buffer = e.target.result,
30545                     dataView = new DataView(buffer),
30546                     offset = 2,
30547                     maxOffset = dataView.byteLength - 4,
30548                     markerBytes,
30549                     markerLength;
30550                 
30551                 if (dataView.getUint16(0) === 0xffd8) {
30552                     while (offset < maxOffset) {
30553                         markerBytes = dataView.getUint16(offset);
30554                         
30555                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30556                             markerLength = dataView.getUint16(offset + 2) + 2;
30557                             if (offset + markerLength > dataView.byteLength) {
30558                                 Roo.log('Invalid meta data: Invalid segment size.');
30559                                 break;
30560                             }
30561                             
30562                             if(markerBytes == 0xffe1){
30563                                 _this.parseExifData(
30564                                     dataView,
30565                                     offset,
30566                                     markerLength
30567                                 );
30568                             }
30569                             
30570                             offset += markerLength;
30571                             
30572                             continue;
30573                         }
30574                         
30575                         break;
30576                     }
30577                     
30578                 }
30579                 
30580                 var url = _this.urlAPI.createObjectURL(_this.file);
30581                 
30582                 _this.loadCanvas(url);
30583                 
30584                 return;
30585             }
30586             
30587             reader.readAsArrayBuffer(this.file);
30588             
30589         }
30590         
30591     },
30592     
30593     parseExifData : function(dataView, offset, length)
30594     {
30595         var tiffOffset = offset + 10,
30596             littleEndian,
30597             dirOffset;
30598     
30599         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30600             // No Exif data, might be XMP data instead
30601             return;
30602         }
30603         
30604         // Check for the ASCII code for "Exif" (0x45786966):
30605         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30606             // No Exif data, might be XMP data instead
30607             return;
30608         }
30609         if (tiffOffset + 8 > dataView.byteLength) {
30610             Roo.log('Invalid Exif data: Invalid segment size.');
30611             return;
30612         }
30613         // Check for the two null bytes:
30614         if (dataView.getUint16(offset + 8) !== 0x0000) {
30615             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30616             return;
30617         }
30618         // Check the byte alignment:
30619         switch (dataView.getUint16(tiffOffset)) {
30620         case 0x4949:
30621             littleEndian = true;
30622             break;
30623         case 0x4D4D:
30624             littleEndian = false;
30625             break;
30626         default:
30627             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30628             return;
30629         }
30630         // Check for the TIFF tag marker (0x002A):
30631         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30632             Roo.log('Invalid Exif data: Missing TIFF marker.');
30633             return;
30634         }
30635         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30636         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30637         
30638         this.parseExifTags(
30639             dataView,
30640             tiffOffset,
30641             tiffOffset + dirOffset,
30642             littleEndian
30643         );
30644     },
30645     
30646     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30647     {
30648         var tagsNumber,
30649             dirEndOffset,
30650             i;
30651         if (dirOffset + 6 > dataView.byteLength) {
30652             Roo.log('Invalid Exif data: Invalid directory offset.');
30653             return;
30654         }
30655         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30656         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30657         if (dirEndOffset + 4 > dataView.byteLength) {
30658             Roo.log('Invalid Exif data: Invalid directory size.');
30659             return;
30660         }
30661         for (i = 0; i < tagsNumber; i += 1) {
30662             this.parseExifTag(
30663                 dataView,
30664                 tiffOffset,
30665                 dirOffset + 2 + 12 * i, // tag offset
30666                 littleEndian
30667             );
30668         }
30669         // Return the offset to the next directory:
30670         return dataView.getUint32(dirEndOffset, littleEndian);
30671     },
30672     
30673     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30674     {
30675         var tag = dataView.getUint16(offset, littleEndian);
30676         
30677         this.exif[tag] = this.getExifValue(
30678             dataView,
30679             tiffOffset,
30680             offset,
30681             dataView.getUint16(offset + 2, littleEndian), // tag type
30682             dataView.getUint32(offset + 4, littleEndian), // tag length
30683             littleEndian
30684         );
30685     },
30686     
30687     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30688     {
30689         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30690             tagSize,
30691             dataOffset,
30692             values,
30693             i,
30694             str,
30695             c;
30696     
30697         if (!tagType) {
30698             Roo.log('Invalid Exif data: Invalid tag type.');
30699             return;
30700         }
30701         
30702         tagSize = tagType.size * length;
30703         // Determine if the value is contained in the dataOffset bytes,
30704         // or if the value at the dataOffset is a pointer to the actual data:
30705         dataOffset = tagSize > 4 ?
30706                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30707         if (dataOffset + tagSize > dataView.byteLength) {
30708             Roo.log('Invalid Exif data: Invalid data offset.');
30709             return;
30710         }
30711         if (length === 1) {
30712             return tagType.getValue(dataView, dataOffset, littleEndian);
30713         }
30714         values = [];
30715         for (i = 0; i < length; i += 1) {
30716             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30717         }
30718         
30719         if (tagType.ascii) {
30720             str = '';
30721             // Concatenate the chars:
30722             for (i = 0; i < values.length; i += 1) {
30723                 c = values[i];
30724                 // Ignore the terminating NULL byte(s):
30725                 if (c === '\u0000') {
30726                     break;
30727                 }
30728                 str += c;
30729             }
30730             return str;
30731         }
30732         return values;
30733     }
30734     
30735 });
30736
30737 Roo.apply(Roo.bootstrap.UploadCropbox, {
30738     tags : {
30739         'Orientation': 0x0112
30740     },
30741     
30742     Orientation: {
30743             1: 0, //'top-left',
30744 //            2: 'top-right',
30745             3: 180, //'bottom-right',
30746 //            4: 'bottom-left',
30747 //            5: 'left-top',
30748             6: 90, //'right-top',
30749 //            7: 'right-bottom',
30750             8: 270 //'left-bottom'
30751     },
30752     
30753     exifTagTypes : {
30754         // byte, 8-bit unsigned int:
30755         1: {
30756             getValue: function (dataView, dataOffset) {
30757                 return dataView.getUint8(dataOffset);
30758             },
30759             size: 1
30760         },
30761         // ascii, 8-bit byte:
30762         2: {
30763             getValue: function (dataView, dataOffset) {
30764                 return String.fromCharCode(dataView.getUint8(dataOffset));
30765             },
30766             size: 1,
30767             ascii: true
30768         },
30769         // short, 16 bit int:
30770         3: {
30771             getValue: function (dataView, dataOffset, littleEndian) {
30772                 return dataView.getUint16(dataOffset, littleEndian);
30773             },
30774             size: 2
30775         },
30776         // long, 32 bit int:
30777         4: {
30778             getValue: function (dataView, dataOffset, littleEndian) {
30779                 return dataView.getUint32(dataOffset, littleEndian);
30780             },
30781             size: 4
30782         },
30783         // rational = two long values, first is numerator, second is denominator:
30784         5: {
30785             getValue: function (dataView, dataOffset, littleEndian) {
30786                 return dataView.getUint32(dataOffset, littleEndian) /
30787                     dataView.getUint32(dataOffset + 4, littleEndian);
30788             },
30789             size: 8
30790         },
30791         // slong, 32 bit signed int:
30792         9: {
30793             getValue: function (dataView, dataOffset, littleEndian) {
30794                 return dataView.getInt32(dataOffset, littleEndian);
30795             },
30796             size: 4
30797         },
30798         // srational, two slongs, first is numerator, second is denominator:
30799         10: {
30800             getValue: function (dataView, dataOffset, littleEndian) {
30801                 return dataView.getInt32(dataOffset, littleEndian) /
30802                     dataView.getInt32(dataOffset + 4, littleEndian);
30803             },
30804             size: 8
30805         }
30806     },
30807     
30808     footer : {
30809         STANDARD : [
30810             {
30811                 tag : 'div',
30812                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30813                 action : 'rotate-left',
30814                 cn : [
30815                     {
30816                         tag : 'button',
30817                         cls : 'btn btn-default',
30818                         html : '<i class="fa fa-undo"></i>'
30819                     }
30820                 ]
30821             },
30822             {
30823                 tag : 'div',
30824                 cls : 'btn-group roo-upload-cropbox-picture',
30825                 action : 'picture',
30826                 cn : [
30827                     {
30828                         tag : 'button',
30829                         cls : 'btn btn-default',
30830                         html : '<i class="fa fa-picture-o"></i>'
30831                     }
30832                 ]
30833             },
30834             {
30835                 tag : 'div',
30836                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30837                 action : 'rotate-right',
30838                 cn : [
30839                     {
30840                         tag : 'button',
30841                         cls : 'btn btn-default',
30842                         html : '<i class="fa fa-repeat"></i>'
30843                     }
30844                 ]
30845             }
30846         ],
30847         DOCUMENT : [
30848             {
30849                 tag : 'div',
30850                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30851                 action : 'rotate-left',
30852                 cn : [
30853                     {
30854                         tag : 'button',
30855                         cls : 'btn btn-default',
30856                         html : '<i class="fa fa-undo"></i>'
30857                     }
30858                 ]
30859             },
30860             {
30861                 tag : 'div',
30862                 cls : 'btn-group roo-upload-cropbox-download',
30863                 action : 'download',
30864                 cn : [
30865                     {
30866                         tag : 'button',
30867                         cls : 'btn btn-default',
30868                         html : '<i class="fa fa-download"></i>'
30869                     }
30870                 ]
30871             },
30872             {
30873                 tag : 'div',
30874                 cls : 'btn-group roo-upload-cropbox-crop',
30875                 action : 'crop',
30876                 cn : [
30877                     {
30878                         tag : 'button',
30879                         cls : 'btn btn-default',
30880                         html : '<i class="fa fa-crop"></i>'
30881                     }
30882                 ]
30883             },
30884             {
30885                 tag : 'div',
30886                 cls : 'btn-group roo-upload-cropbox-trash',
30887                 action : 'trash',
30888                 cn : [
30889                     {
30890                         tag : 'button',
30891                         cls : 'btn btn-default',
30892                         html : '<i class="fa fa-trash"></i>'
30893                     }
30894                 ]
30895             },
30896             {
30897                 tag : 'div',
30898                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30899                 action : 'rotate-right',
30900                 cn : [
30901                     {
30902                         tag : 'button',
30903                         cls : 'btn btn-default',
30904                         html : '<i class="fa fa-repeat"></i>'
30905                     }
30906                 ]
30907             }
30908         ],
30909         ROTATOR : [
30910             {
30911                 tag : 'div',
30912                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30913                 action : 'rotate-left',
30914                 cn : [
30915                     {
30916                         tag : 'button',
30917                         cls : 'btn btn-default',
30918                         html : '<i class="fa fa-undo"></i>'
30919                     }
30920                 ]
30921             },
30922             {
30923                 tag : 'div',
30924                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30925                 action : 'rotate-right',
30926                 cn : [
30927                     {
30928                         tag : 'button',
30929                         cls : 'btn btn-default',
30930                         html : '<i class="fa fa-repeat"></i>'
30931                     }
30932                 ]
30933             }
30934         ]
30935     }
30936 });
30937
30938 /*
30939 * Licence: LGPL
30940 */
30941
30942 /**
30943  * @class Roo.bootstrap.DocumentManager
30944  * @extends Roo.bootstrap.Component
30945  * Bootstrap DocumentManager class
30946  * @cfg {String} paramName default 'imageUpload'
30947  * @cfg {String} toolTipName default 'filename'
30948  * @cfg {String} method default POST
30949  * @cfg {String} url action url
30950  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30951  * @cfg {Boolean} multiple multiple upload default true
30952  * @cfg {Number} thumbSize default 300
30953  * @cfg {String} fieldLabel
30954  * @cfg {Number} labelWidth default 4
30955  * @cfg {String} labelAlign (left|top) default left
30956  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30957 * @cfg {Number} labellg set the width of label (1-12)
30958  * @cfg {Number} labelmd set the width of label (1-12)
30959  * @cfg {Number} labelsm set the width of label (1-12)
30960  * @cfg {Number} labelxs set the width of label (1-12)
30961  * 
30962  * @constructor
30963  * Create a new DocumentManager
30964  * @param {Object} config The config object
30965  */
30966
30967 Roo.bootstrap.DocumentManager = function(config){
30968     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30969     
30970     this.files = [];
30971     this.delegates = [];
30972     
30973     this.addEvents({
30974         /**
30975          * @event initial
30976          * Fire when initial the DocumentManager
30977          * @param {Roo.bootstrap.DocumentManager} this
30978          */
30979         "initial" : true,
30980         /**
30981          * @event inspect
30982          * inspect selected file
30983          * @param {Roo.bootstrap.DocumentManager} this
30984          * @param {File} file
30985          */
30986         "inspect" : true,
30987         /**
30988          * @event exception
30989          * Fire when xhr load exception
30990          * @param {Roo.bootstrap.DocumentManager} this
30991          * @param {XMLHttpRequest} xhr
30992          */
30993         "exception" : true,
30994         /**
30995          * @event afterupload
30996          * Fire when xhr load exception
30997          * @param {Roo.bootstrap.DocumentManager} this
30998          * @param {XMLHttpRequest} xhr
30999          */
31000         "afterupload" : true,
31001         /**
31002          * @event prepare
31003          * prepare the form data
31004          * @param {Roo.bootstrap.DocumentManager} this
31005          * @param {Object} formData
31006          */
31007         "prepare" : true,
31008         /**
31009          * @event remove
31010          * Fire when remove the file
31011          * @param {Roo.bootstrap.DocumentManager} this
31012          * @param {Object} file
31013          */
31014         "remove" : true,
31015         /**
31016          * @event refresh
31017          * Fire after refresh the file
31018          * @param {Roo.bootstrap.DocumentManager} this
31019          */
31020         "refresh" : true,
31021         /**
31022          * @event click
31023          * Fire after click the image
31024          * @param {Roo.bootstrap.DocumentManager} this
31025          * @param {Object} file
31026          */
31027         "click" : true,
31028         /**
31029          * @event edit
31030          * Fire when upload a image and editable set to true
31031          * @param {Roo.bootstrap.DocumentManager} this
31032          * @param {Object} file
31033          */
31034         "edit" : true,
31035         /**
31036          * @event beforeselectfile
31037          * Fire before select file
31038          * @param {Roo.bootstrap.DocumentManager} this
31039          */
31040         "beforeselectfile" : true,
31041         /**
31042          * @event process
31043          * Fire before process file
31044          * @param {Roo.bootstrap.DocumentManager} this
31045          * @param {Object} file
31046          */
31047         "process" : true,
31048         /**
31049          * @event previewrendered
31050          * Fire when preview rendered
31051          * @param {Roo.bootstrap.DocumentManager} this
31052          * @param {Object} file
31053          */
31054         "previewrendered" : true,
31055         /**
31056          */
31057         "previewResize" : true
31058         
31059     });
31060 };
31061
31062 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31063     
31064     boxes : 0,
31065     inputName : '',
31066     thumbSize : 300,
31067     multiple : true,
31068     files : false,
31069     method : 'POST',
31070     url : '',
31071     paramName : 'imageUpload',
31072     toolTipName : 'filename',
31073     fieldLabel : '',
31074     labelWidth : 4,
31075     labelAlign : 'left',
31076     editable : true,
31077     delegates : false,
31078     xhr : false, 
31079     
31080     labellg : 0,
31081     labelmd : 0,
31082     labelsm : 0,
31083     labelxs : 0,
31084     
31085     getAutoCreate : function()
31086     {   
31087         var managerWidget = {
31088             tag : 'div',
31089             cls : 'roo-document-manager',
31090             cn : [
31091                 {
31092                     tag : 'input',
31093                     cls : 'roo-document-manager-selector',
31094                     type : 'file'
31095                 },
31096                 {
31097                     tag : 'div',
31098                     cls : 'roo-document-manager-uploader',
31099                     cn : [
31100                         {
31101                             tag : 'div',
31102                             cls : 'roo-document-manager-upload-btn',
31103                             html : '<i class="fa fa-plus"></i>'
31104                         }
31105                     ]
31106                     
31107                 }
31108             ]
31109         };
31110         
31111         var content = [
31112             {
31113                 tag : 'div',
31114                 cls : 'column col-md-12',
31115                 cn : managerWidget
31116             }
31117         ];
31118         
31119         if(this.fieldLabel.length){
31120             
31121             content = [
31122                 {
31123                     tag : 'div',
31124                     cls : 'column col-md-12',
31125                     html : this.fieldLabel
31126                 },
31127                 {
31128                     tag : 'div',
31129                     cls : 'column col-md-12',
31130                     cn : managerWidget
31131                 }
31132             ];
31133
31134             if(this.labelAlign == 'left'){
31135                 content = [
31136                     {
31137                         tag : 'div',
31138                         cls : 'column',
31139                         html : this.fieldLabel
31140                     },
31141                     {
31142                         tag : 'div',
31143                         cls : 'column',
31144                         cn : managerWidget
31145                     }
31146                 ];
31147                 
31148                 if(this.labelWidth > 12){
31149                     content[0].style = "width: " + this.labelWidth + 'px';
31150                 }
31151
31152                 if(this.labelWidth < 13 && this.labelmd == 0){
31153                     this.labelmd = this.labelWidth;
31154                 }
31155
31156                 if(this.labellg > 0){
31157                     content[0].cls += ' col-lg-' + this.labellg;
31158                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31159                 }
31160
31161                 if(this.labelmd > 0){
31162                     content[0].cls += ' col-md-' + this.labelmd;
31163                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31164                 }
31165
31166                 if(this.labelsm > 0){
31167                     content[0].cls += ' col-sm-' + this.labelsm;
31168                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31169                 }
31170
31171                 if(this.labelxs > 0){
31172                     content[0].cls += ' col-xs-' + this.labelxs;
31173                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31174                 }
31175                 
31176             }
31177         }
31178         
31179         var cfg = {
31180             tag : 'div',
31181             cls : 'row clearfix',
31182             cn : content
31183         };
31184         
31185         return cfg;
31186         
31187     },
31188     
31189     initEvents : function()
31190     {
31191         this.managerEl = this.el.select('.roo-document-manager', true).first();
31192         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31193         
31194         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31195         this.selectorEl.hide();
31196         
31197         if(this.multiple){
31198             this.selectorEl.attr('multiple', 'multiple');
31199         }
31200         
31201         this.selectorEl.on('change', this.onFileSelected, this);
31202         
31203         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31204         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31205         
31206         this.uploader.on('click', this.onUploaderClick, this);
31207         
31208         this.renderProgressDialog();
31209         
31210         var _this = this;
31211         
31212         window.addEventListener("resize", function() { _this.refresh(); } );
31213         
31214         this.fireEvent('initial', this);
31215     },
31216     
31217     renderProgressDialog : function()
31218     {
31219         var _this = this;
31220         
31221         this.progressDialog = new Roo.bootstrap.Modal({
31222             cls : 'roo-document-manager-progress-dialog',
31223             allow_close : false,
31224             animate : false,
31225             title : '',
31226             buttons : [
31227                 {
31228                     name  :'cancel',
31229                     weight : 'danger',
31230                     html : 'Cancel'
31231                 }
31232             ], 
31233             listeners : { 
31234                 btnclick : function() {
31235                     _this.uploadCancel();
31236                     this.hide();
31237                 }
31238             }
31239         });
31240          
31241         this.progressDialog.render(Roo.get(document.body));
31242          
31243         this.progress = new Roo.bootstrap.Progress({
31244             cls : 'roo-document-manager-progress',
31245             active : true,
31246             striped : true
31247         });
31248         
31249         this.progress.render(this.progressDialog.getChildContainer());
31250         
31251         this.progressBar = new Roo.bootstrap.ProgressBar({
31252             cls : 'roo-document-manager-progress-bar',
31253             aria_valuenow : 0,
31254             aria_valuemin : 0,
31255             aria_valuemax : 12,
31256             panel : 'success'
31257         });
31258         
31259         this.progressBar.render(this.progress.getChildContainer());
31260     },
31261     
31262     onUploaderClick : function(e)
31263     {
31264         e.preventDefault();
31265      
31266         if(this.fireEvent('beforeselectfile', this) != false){
31267             this.selectorEl.dom.click();
31268         }
31269         
31270     },
31271     
31272     onFileSelected : function(e)
31273     {
31274         e.preventDefault();
31275         
31276         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31277             return;
31278         }
31279         
31280         Roo.each(this.selectorEl.dom.files, function(file){
31281             if(this.fireEvent('inspect', this, file) != false){
31282                 this.files.push(file);
31283             }
31284         }, this);
31285         
31286         this.queue();
31287         
31288     },
31289     
31290     queue : function()
31291     {
31292         this.selectorEl.dom.value = '';
31293         
31294         if(!this.files || !this.files.length){
31295             return;
31296         }
31297         
31298         if(this.boxes > 0 && this.files.length > this.boxes){
31299             this.files = this.files.slice(0, this.boxes);
31300         }
31301         
31302         this.uploader.show();
31303         
31304         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31305             this.uploader.hide();
31306         }
31307         
31308         var _this = this;
31309         
31310         var files = [];
31311         
31312         var docs = [];
31313         
31314         Roo.each(this.files, function(file){
31315             
31316             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31317                 var f = this.renderPreview(file);
31318                 files.push(f);
31319                 return;
31320             }
31321             
31322             if(file.type.indexOf('image') != -1){
31323                 this.delegates.push(
31324                     (function(){
31325                         _this.process(file);
31326                     }).createDelegate(this)
31327                 );
31328         
31329                 return;
31330             }
31331             
31332             docs.push(
31333                 (function(){
31334                     _this.process(file);
31335                 }).createDelegate(this)
31336             );
31337             
31338         }, this);
31339         
31340         this.files = files;
31341         
31342         this.delegates = this.delegates.concat(docs);
31343         
31344         if(!this.delegates.length){
31345             this.refresh();
31346             return;
31347         }
31348         
31349         this.progressBar.aria_valuemax = this.delegates.length;
31350         
31351         this.arrange();
31352         
31353         return;
31354     },
31355     
31356     arrange : function()
31357     {
31358         if(!this.delegates.length){
31359             this.progressDialog.hide();
31360             this.refresh();
31361             return;
31362         }
31363         
31364         var delegate = this.delegates.shift();
31365         
31366         this.progressDialog.show();
31367         
31368         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31369         
31370         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31371         
31372         delegate();
31373     },
31374     
31375     refresh : function()
31376     {
31377         this.uploader.show();
31378         
31379         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31380             this.uploader.hide();
31381         }
31382         
31383         Roo.isTouch ? this.closable(false) : this.closable(true);
31384         
31385         this.fireEvent('refresh', this);
31386     },
31387     
31388     onRemove : function(e, el, o)
31389     {
31390         e.preventDefault();
31391         
31392         this.fireEvent('remove', this, o);
31393         
31394     },
31395     
31396     remove : function(o)
31397     {
31398         var files = [];
31399         
31400         Roo.each(this.files, function(file){
31401             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31402                 files.push(file);
31403                 return;
31404             }
31405
31406             o.target.remove();
31407
31408         }, this);
31409         
31410         this.files = files;
31411         
31412         this.refresh();
31413     },
31414     
31415     clear : function()
31416     {
31417         Roo.each(this.files, function(file){
31418             if(!file.target){
31419                 return;
31420             }
31421             
31422             file.target.remove();
31423
31424         }, this);
31425         
31426         this.files = [];
31427         
31428         this.refresh();
31429     },
31430     
31431     onClick : function(e, el, o)
31432     {
31433         e.preventDefault();
31434         
31435         this.fireEvent('click', this, o);
31436         
31437     },
31438     
31439     closable : function(closable)
31440     {
31441         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31442             
31443             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31444             
31445             if(closable){
31446                 el.show();
31447                 return;
31448             }
31449             
31450             el.hide();
31451             
31452         }, this);
31453     },
31454     
31455     xhrOnLoad : function(xhr)
31456     {
31457         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31458             el.remove();
31459         }, this);
31460         
31461         if (xhr.readyState !== 4) {
31462             this.arrange();
31463             this.fireEvent('exception', this, xhr);
31464             return;
31465         }
31466
31467         var response = Roo.decode(xhr.responseText);
31468         
31469         if(!response.success){
31470             this.arrange();
31471             this.fireEvent('exception', this, xhr);
31472             return;
31473         }
31474         
31475         var file = this.renderPreview(response.data);
31476         
31477         this.files.push(file);
31478         
31479         this.arrange();
31480         
31481         this.fireEvent('afterupload', this, xhr);
31482         
31483     },
31484     
31485     xhrOnError : function(xhr)
31486     {
31487         Roo.log('xhr on error');
31488         
31489         var response = Roo.decode(xhr.responseText);
31490           
31491         Roo.log(response);
31492         
31493         this.arrange();
31494     },
31495     
31496     process : function(file)
31497     {
31498         if(this.fireEvent('process', this, file) !== false){
31499             if(this.editable && file.type.indexOf('image') != -1){
31500                 this.fireEvent('edit', this, file);
31501                 return;
31502             }
31503
31504             this.uploadStart(file, false);
31505
31506             return;
31507         }
31508         
31509     },
31510     
31511     uploadStart : function(file, crop)
31512     {
31513         this.xhr = new XMLHttpRequest();
31514         
31515         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31516             this.arrange();
31517             return;
31518         }
31519         
31520         file.xhr = this.xhr;
31521             
31522         this.managerEl.createChild({
31523             tag : 'div',
31524             cls : 'roo-document-manager-loading',
31525             cn : [
31526                 {
31527                     tag : 'div',
31528                     tooltip : file.name,
31529                     cls : 'roo-document-manager-thumb',
31530                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31531                 }
31532             ]
31533
31534         });
31535
31536         this.xhr.open(this.method, this.url, true);
31537         
31538         var headers = {
31539             "Accept": "application/json",
31540             "Cache-Control": "no-cache",
31541             "X-Requested-With": "XMLHttpRequest"
31542         };
31543         
31544         for (var headerName in headers) {
31545             var headerValue = headers[headerName];
31546             if (headerValue) {
31547                 this.xhr.setRequestHeader(headerName, headerValue);
31548             }
31549         }
31550         
31551         var _this = this;
31552         
31553         this.xhr.onload = function()
31554         {
31555             _this.xhrOnLoad(_this.xhr);
31556         }
31557         
31558         this.xhr.onerror = function()
31559         {
31560             _this.xhrOnError(_this.xhr);
31561         }
31562         
31563         var formData = new FormData();
31564
31565         formData.append('returnHTML', 'NO');
31566         
31567         if(crop){
31568             formData.append('crop', crop);
31569         }
31570         
31571         formData.append(this.paramName, file, file.name);
31572         
31573         var options = {
31574             file : file, 
31575             manually : false
31576         };
31577         
31578         if(this.fireEvent('prepare', this, formData, options) != false){
31579             
31580             if(options.manually){
31581                 return;
31582             }
31583             
31584             this.xhr.send(formData);
31585             return;
31586         };
31587         
31588         this.uploadCancel();
31589     },
31590     
31591     uploadCancel : function()
31592     {
31593         if (this.xhr) {
31594             this.xhr.abort();
31595         }
31596         
31597         this.delegates = [];
31598         
31599         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31600             el.remove();
31601         }, this);
31602         
31603         this.arrange();
31604     },
31605     
31606     renderPreview : function(file)
31607     {
31608         if(typeof(file.target) != 'undefined' && file.target){
31609             return file;
31610         }
31611         
31612         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31613         
31614         var previewEl = this.managerEl.createChild({
31615             tag : 'div',
31616             cls : 'roo-document-manager-preview',
31617             cn : [
31618                 {
31619                     tag : 'div',
31620                     tooltip : file[this.toolTipName],
31621                     cls : 'roo-document-manager-thumb',
31622                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31623                 },
31624                 {
31625                     tag : 'button',
31626                     cls : 'close',
31627                     html : '<i class="fa fa-times-circle"></i>'
31628                 }
31629             ]
31630         });
31631
31632         var close = previewEl.select('button.close', true).first();
31633
31634         close.on('click', this.onRemove, this, file);
31635
31636         file.target = previewEl;
31637
31638         var image = previewEl.select('img', true).first();
31639         
31640         var _this = this;
31641         
31642         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31643         
31644         image.on('click', this.onClick, this, file);
31645         
31646         this.fireEvent('previewrendered', this, file);
31647         
31648         return file;
31649         
31650     },
31651     
31652     onPreviewLoad : function(file, image)
31653     {
31654         if(typeof(file.target) == 'undefined' || !file.target){
31655             return;
31656         }
31657         
31658         var width = image.dom.naturalWidth || image.dom.width;
31659         var height = image.dom.naturalHeight || image.dom.height;
31660         
31661         if(!this.previewResize) {
31662             return;
31663         }
31664         
31665         if(width > height){
31666             file.target.addClass('wide');
31667             return;
31668         }
31669         
31670         file.target.addClass('tall');
31671         return;
31672         
31673     },
31674     
31675     uploadFromSource : function(file, crop)
31676     {
31677         this.xhr = new XMLHttpRequest();
31678         
31679         this.managerEl.createChild({
31680             tag : 'div',
31681             cls : 'roo-document-manager-loading',
31682             cn : [
31683                 {
31684                     tag : 'div',
31685                     tooltip : file.name,
31686                     cls : 'roo-document-manager-thumb',
31687                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31688                 }
31689             ]
31690
31691         });
31692
31693         this.xhr.open(this.method, this.url, true);
31694         
31695         var headers = {
31696             "Accept": "application/json",
31697             "Cache-Control": "no-cache",
31698             "X-Requested-With": "XMLHttpRequest"
31699         };
31700         
31701         for (var headerName in headers) {
31702             var headerValue = headers[headerName];
31703             if (headerValue) {
31704                 this.xhr.setRequestHeader(headerName, headerValue);
31705             }
31706         }
31707         
31708         var _this = this;
31709         
31710         this.xhr.onload = function()
31711         {
31712             _this.xhrOnLoad(_this.xhr);
31713         }
31714         
31715         this.xhr.onerror = function()
31716         {
31717             _this.xhrOnError(_this.xhr);
31718         }
31719         
31720         var formData = new FormData();
31721
31722         formData.append('returnHTML', 'NO');
31723         
31724         formData.append('crop', crop);
31725         
31726         if(typeof(file.filename) != 'undefined'){
31727             formData.append('filename', file.filename);
31728         }
31729         
31730         if(typeof(file.mimetype) != 'undefined'){
31731             formData.append('mimetype', file.mimetype);
31732         }
31733         
31734         Roo.log(formData);
31735         
31736         if(this.fireEvent('prepare', this, formData) != false){
31737             this.xhr.send(formData);
31738         };
31739     }
31740 });
31741
31742 /*
31743 * Licence: LGPL
31744 */
31745
31746 /**
31747  * @class Roo.bootstrap.DocumentViewer
31748  * @extends Roo.bootstrap.Component
31749  * Bootstrap DocumentViewer class
31750  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31751  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31752  * 
31753  * @constructor
31754  * Create a new DocumentViewer
31755  * @param {Object} config The config object
31756  */
31757
31758 Roo.bootstrap.DocumentViewer = function(config){
31759     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31760     
31761     this.addEvents({
31762         /**
31763          * @event initial
31764          * Fire after initEvent
31765          * @param {Roo.bootstrap.DocumentViewer} this
31766          */
31767         "initial" : true,
31768         /**
31769          * @event click
31770          * Fire after click
31771          * @param {Roo.bootstrap.DocumentViewer} this
31772          */
31773         "click" : true,
31774         /**
31775          * @event download
31776          * Fire after download button
31777          * @param {Roo.bootstrap.DocumentViewer} this
31778          */
31779         "download" : true,
31780         /**
31781          * @event trash
31782          * Fire after trash button
31783          * @param {Roo.bootstrap.DocumentViewer} this
31784          */
31785         "trash" : true
31786         
31787     });
31788 };
31789
31790 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31791     
31792     showDownload : true,
31793     
31794     showTrash : true,
31795     
31796     getAutoCreate : function()
31797     {
31798         var cfg = {
31799             tag : 'div',
31800             cls : 'roo-document-viewer',
31801             cn : [
31802                 {
31803                     tag : 'div',
31804                     cls : 'roo-document-viewer-body',
31805                     cn : [
31806                         {
31807                             tag : 'div',
31808                             cls : 'roo-document-viewer-thumb',
31809                             cn : [
31810                                 {
31811                                     tag : 'img',
31812                                     cls : 'roo-document-viewer-image'
31813                                 }
31814                             ]
31815                         }
31816                     ]
31817                 },
31818                 {
31819                     tag : 'div',
31820                     cls : 'roo-document-viewer-footer',
31821                     cn : {
31822                         tag : 'div',
31823                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31824                         cn : [
31825                             {
31826                                 tag : 'div',
31827                                 cls : 'btn-group roo-document-viewer-download',
31828                                 cn : [
31829                                     {
31830                                         tag : 'button',
31831                                         cls : 'btn btn-default',
31832                                         html : '<i class="fa fa-download"></i>'
31833                                     }
31834                                 ]
31835                             },
31836                             {
31837                                 tag : 'div',
31838                                 cls : 'btn-group roo-document-viewer-trash',
31839                                 cn : [
31840                                     {
31841                                         tag : 'button',
31842                                         cls : 'btn btn-default',
31843                                         html : '<i class="fa fa-trash"></i>'
31844                                     }
31845                                 ]
31846                             }
31847                         ]
31848                     }
31849                 }
31850             ]
31851         };
31852         
31853         return cfg;
31854     },
31855     
31856     initEvents : function()
31857     {
31858         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31859         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31860         
31861         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31862         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31863         
31864         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31865         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31866         
31867         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31868         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31869         
31870         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31871         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31872         
31873         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31874         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31875         
31876         this.bodyEl.on('click', this.onClick, this);
31877         this.downloadBtn.on('click', this.onDownload, this);
31878         this.trashBtn.on('click', this.onTrash, this);
31879         
31880         this.downloadBtn.hide();
31881         this.trashBtn.hide();
31882         
31883         if(this.showDownload){
31884             this.downloadBtn.show();
31885         }
31886         
31887         if(this.showTrash){
31888             this.trashBtn.show();
31889         }
31890         
31891         if(!this.showDownload && !this.showTrash) {
31892             this.footerEl.hide();
31893         }
31894         
31895     },
31896     
31897     initial : function()
31898     {
31899         this.fireEvent('initial', this);
31900         
31901     },
31902     
31903     onClick : function(e)
31904     {
31905         e.preventDefault();
31906         
31907         this.fireEvent('click', this);
31908     },
31909     
31910     onDownload : function(e)
31911     {
31912         e.preventDefault();
31913         
31914         this.fireEvent('download', this);
31915     },
31916     
31917     onTrash : function(e)
31918     {
31919         e.preventDefault();
31920         
31921         this.fireEvent('trash', this);
31922     }
31923     
31924 });
31925 /*
31926  * - LGPL
31927  *
31928  * nav progress bar
31929  * 
31930  */
31931
31932 /**
31933  * @class Roo.bootstrap.NavProgressBar
31934  * @extends Roo.bootstrap.Component
31935  * Bootstrap NavProgressBar class
31936  * 
31937  * @constructor
31938  * Create a new nav progress bar
31939  * @param {Object} config The config object
31940  */
31941
31942 Roo.bootstrap.NavProgressBar = function(config){
31943     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31944
31945     this.bullets = this.bullets || [];
31946    
31947 //    Roo.bootstrap.NavProgressBar.register(this);
31948      this.addEvents({
31949         /**
31950              * @event changed
31951              * Fires when the active item changes
31952              * @param {Roo.bootstrap.NavProgressBar} this
31953              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31954              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31955          */
31956         'changed': true
31957      });
31958     
31959 };
31960
31961 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31962     
31963     bullets : [],
31964     barItems : [],
31965     
31966     getAutoCreate : function()
31967     {
31968         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31969         
31970         cfg = {
31971             tag : 'div',
31972             cls : 'roo-navigation-bar-group',
31973             cn : [
31974                 {
31975                     tag : 'div',
31976                     cls : 'roo-navigation-top-bar'
31977                 },
31978                 {
31979                     tag : 'div',
31980                     cls : 'roo-navigation-bullets-bar',
31981                     cn : [
31982                         {
31983                             tag : 'ul',
31984                             cls : 'roo-navigation-bar'
31985                         }
31986                     ]
31987                 },
31988                 
31989                 {
31990                     tag : 'div',
31991                     cls : 'roo-navigation-bottom-bar'
31992                 }
31993             ]
31994             
31995         };
31996         
31997         return cfg;
31998         
31999     },
32000     
32001     initEvents: function() 
32002     {
32003         
32004     },
32005     
32006     onRender : function(ct, position) 
32007     {
32008         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32009         
32010         if(this.bullets.length){
32011             Roo.each(this.bullets, function(b){
32012                this.addItem(b);
32013             }, this);
32014         }
32015         
32016         this.format();
32017         
32018     },
32019     
32020     addItem : function(cfg)
32021     {
32022         var item = new Roo.bootstrap.NavProgressItem(cfg);
32023         
32024         item.parentId = this.id;
32025         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32026         
32027         if(cfg.html){
32028             var top = new Roo.bootstrap.Element({
32029                 tag : 'div',
32030                 cls : 'roo-navigation-bar-text'
32031             });
32032             
32033             var bottom = new Roo.bootstrap.Element({
32034                 tag : 'div',
32035                 cls : 'roo-navigation-bar-text'
32036             });
32037             
32038             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32039             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32040             
32041             var topText = new Roo.bootstrap.Element({
32042                 tag : 'span',
32043                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32044             });
32045             
32046             var bottomText = new Roo.bootstrap.Element({
32047                 tag : 'span',
32048                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32049             });
32050             
32051             topText.onRender(top.el, null);
32052             bottomText.onRender(bottom.el, null);
32053             
32054             item.topEl = top;
32055             item.bottomEl = bottom;
32056         }
32057         
32058         this.barItems.push(item);
32059         
32060         return item;
32061     },
32062     
32063     getActive : function()
32064     {
32065         var active = false;
32066         
32067         Roo.each(this.barItems, function(v){
32068             
32069             if (!v.isActive()) {
32070                 return;
32071             }
32072             
32073             active = v;
32074             return false;
32075             
32076         });
32077         
32078         return active;
32079     },
32080     
32081     setActiveItem : function(item)
32082     {
32083         var prev = false;
32084         
32085         Roo.each(this.barItems, function(v){
32086             if (v.rid == item.rid) {
32087                 return ;
32088             }
32089             
32090             if (v.isActive()) {
32091                 v.setActive(false);
32092                 prev = v;
32093             }
32094         });
32095
32096         item.setActive(true);
32097         
32098         this.fireEvent('changed', this, item, prev);
32099     },
32100     
32101     getBarItem: function(rid)
32102     {
32103         var ret = false;
32104         
32105         Roo.each(this.barItems, function(e) {
32106             if (e.rid != rid) {
32107                 return;
32108             }
32109             
32110             ret =  e;
32111             return false;
32112         });
32113         
32114         return ret;
32115     },
32116     
32117     indexOfItem : function(item)
32118     {
32119         var index = false;
32120         
32121         Roo.each(this.barItems, function(v, i){
32122             
32123             if (v.rid != item.rid) {
32124                 return;
32125             }
32126             
32127             index = i;
32128             return false
32129         });
32130         
32131         return index;
32132     },
32133     
32134     setActiveNext : function()
32135     {
32136         var i = this.indexOfItem(this.getActive());
32137         
32138         if (i > this.barItems.length) {
32139             return;
32140         }
32141         
32142         this.setActiveItem(this.barItems[i+1]);
32143     },
32144     
32145     setActivePrev : function()
32146     {
32147         var i = this.indexOfItem(this.getActive());
32148         
32149         if (i  < 1) {
32150             return;
32151         }
32152         
32153         this.setActiveItem(this.barItems[i-1]);
32154     },
32155     
32156     format : function()
32157     {
32158         if(!this.barItems.length){
32159             return;
32160         }
32161      
32162         var width = 100 / this.barItems.length;
32163         
32164         Roo.each(this.barItems, function(i){
32165             i.el.setStyle('width', width + '%');
32166             i.topEl.el.setStyle('width', width + '%');
32167             i.bottomEl.el.setStyle('width', width + '%');
32168         }, this);
32169         
32170     }
32171     
32172 });
32173 /*
32174  * - LGPL
32175  *
32176  * Nav Progress Item
32177  * 
32178  */
32179
32180 /**
32181  * @class Roo.bootstrap.NavProgressItem
32182  * @extends Roo.bootstrap.Component
32183  * Bootstrap NavProgressItem class
32184  * @cfg {String} rid the reference id
32185  * @cfg {Boolean} active (true|false) Is item active default false
32186  * @cfg {Boolean} disabled (true|false) Is item active default false
32187  * @cfg {String} html
32188  * @cfg {String} position (top|bottom) text position default bottom
32189  * @cfg {String} icon show icon instead of number
32190  * 
32191  * @constructor
32192  * Create a new NavProgressItem
32193  * @param {Object} config The config object
32194  */
32195 Roo.bootstrap.NavProgressItem = function(config){
32196     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32197     this.addEvents({
32198         // raw events
32199         /**
32200          * @event click
32201          * The raw click event for the entire grid.
32202          * @param {Roo.bootstrap.NavProgressItem} this
32203          * @param {Roo.EventObject} e
32204          */
32205         "click" : true
32206     });
32207    
32208 };
32209
32210 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32211     
32212     rid : '',
32213     active : false,
32214     disabled : false,
32215     html : '',
32216     position : 'bottom',
32217     icon : false,
32218     
32219     getAutoCreate : function()
32220     {
32221         var iconCls = 'roo-navigation-bar-item-icon';
32222         
32223         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32224         
32225         var cfg = {
32226             tag: 'li',
32227             cls: 'roo-navigation-bar-item',
32228             cn : [
32229                 {
32230                     tag : 'i',
32231                     cls : iconCls
32232                 }
32233             ]
32234         };
32235         
32236         if(this.active){
32237             cfg.cls += ' active';
32238         }
32239         if(this.disabled){
32240             cfg.cls += ' disabled';
32241         }
32242         
32243         return cfg;
32244     },
32245     
32246     disable : function()
32247     {
32248         this.setDisabled(true);
32249     },
32250     
32251     enable : function()
32252     {
32253         this.setDisabled(false);
32254     },
32255     
32256     initEvents: function() 
32257     {
32258         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32259         
32260         this.iconEl.on('click', this.onClick, this);
32261     },
32262     
32263     onClick : function(e)
32264     {
32265         e.preventDefault();
32266         
32267         if(this.disabled){
32268             return;
32269         }
32270         
32271         if(this.fireEvent('click', this, e) === false){
32272             return;
32273         };
32274         
32275         this.parent().setActiveItem(this);
32276     },
32277     
32278     isActive: function () 
32279     {
32280         return this.active;
32281     },
32282     
32283     setActive : function(state)
32284     {
32285         if(this.active == state){
32286             return;
32287         }
32288         
32289         this.active = state;
32290         
32291         if (state) {
32292             this.el.addClass('active');
32293             return;
32294         }
32295         
32296         this.el.removeClass('active');
32297         
32298         return;
32299     },
32300     
32301     setDisabled : function(state)
32302     {
32303         if(this.disabled == state){
32304             return;
32305         }
32306         
32307         this.disabled = state;
32308         
32309         if (state) {
32310             this.el.addClass('disabled');
32311             return;
32312         }
32313         
32314         this.el.removeClass('disabled');
32315     },
32316     
32317     tooltipEl : function()
32318     {
32319         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32320     }
32321 });
32322  
32323
32324  /*
32325  * - LGPL
32326  *
32327  * FieldLabel
32328  * 
32329  */
32330
32331 /**
32332  * @class Roo.bootstrap.FieldLabel
32333  * @extends Roo.bootstrap.Component
32334  * Bootstrap FieldLabel class
32335  * @cfg {String} html contents of the element
32336  * @cfg {String} tag tag of the element default label
32337  * @cfg {String} cls class of the element
32338  * @cfg {String} target label target 
32339  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32340  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32341  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32342  * @cfg {String} iconTooltip default "This field is required"
32343  * @cfg {String} indicatorpos (left|right) default left
32344  * 
32345  * @constructor
32346  * Create a new FieldLabel
32347  * @param {Object} config The config object
32348  */
32349
32350 Roo.bootstrap.FieldLabel = function(config){
32351     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32352     
32353     this.addEvents({
32354             /**
32355              * @event invalid
32356              * Fires after the field has been marked as invalid.
32357              * @param {Roo.form.FieldLabel} this
32358              * @param {String} msg The validation message
32359              */
32360             invalid : true,
32361             /**
32362              * @event valid
32363              * Fires after the field has been validated with no errors.
32364              * @param {Roo.form.FieldLabel} this
32365              */
32366             valid : true
32367         });
32368 };
32369
32370 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32371     
32372     tag: 'label',
32373     cls: '',
32374     html: '',
32375     target: '',
32376     allowBlank : true,
32377     invalidClass : 'has-warning',
32378     validClass : 'has-success',
32379     iconTooltip : 'This field is required',
32380     indicatorpos : 'left',
32381     
32382     getAutoCreate : function(){
32383         
32384         var cls = "";
32385         if (!this.allowBlank) {
32386             cls  = "visible";
32387         }
32388         
32389         var cfg = {
32390             tag : this.tag,
32391             cls : 'roo-bootstrap-field-label ' + this.cls,
32392             for : this.target,
32393             cn : [
32394                 {
32395                     tag : 'i',
32396                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32397                     tooltip : this.iconTooltip
32398                 },
32399                 {
32400                     tag : 'span',
32401                     html : this.html
32402                 }
32403             ] 
32404         };
32405         
32406         if(this.indicatorpos == 'right'){
32407             var cfg = {
32408                 tag : this.tag,
32409                 cls : 'roo-bootstrap-field-label ' + this.cls,
32410                 for : this.target,
32411                 cn : [
32412                     {
32413                         tag : 'span',
32414                         html : this.html
32415                     },
32416                     {
32417                         tag : 'i',
32418                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32419                         tooltip : this.iconTooltip
32420                     }
32421                 ] 
32422             };
32423         }
32424         
32425         return cfg;
32426     },
32427     
32428     initEvents: function() 
32429     {
32430         Roo.bootstrap.Element.superclass.initEvents.call(this);
32431         
32432         this.indicator = this.indicatorEl();
32433         
32434         if(this.indicator){
32435             this.indicator.removeClass('visible');
32436             this.indicator.addClass('invisible');
32437         }
32438         
32439         Roo.bootstrap.FieldLabel.register(this);
32440     },
32441     
32442     indicatorEl : function()
32443     {
32444         var indicator = this.el.select('i.roo-required-indicator',true).first();
32445         
32446         if(!indicator){
32447             return false;
32448         }
32449         
32450         return indicator;
32451         
32452     },
32453     
32454     /**
32455      * Mark this field as valid
32456      */
32457     markValid : function()
32458     {
32459         if(this.indicator){
32460             this.indicator.removeClass('visible');
32461             this.indicator.addClass('invisible');
32462         }
32463         if (Roo.bootstrap.version == 3) {
32464             this.el.removeClass(this.invalidClass);
32465             this.el.addClass(this.validClass);
32466         } else {
32467             this.el.removeClass('is-invalid');
32468             this.el.addClass('is-valid');
32469         }
32470         
32471         
32472         this.fireEvent('valid', this);
32473     },
32474     
32475     /**
32476      * Mark this field as invalid
32477      * @param {String} msg The validation message
32478      */
32479     markInvalid : function(msg)
32480     {
32481         if(this.indicator){
32482             this.indicator.removeClass('invisible');
32483             this.indicator.addClass('visible');
32484         }
32485           if (Roo.bootstrap.version == 3) {
32486             this.el.removeClass(this.validClass);
32487             this.el.addClass(this.invalidClass);
32488         } else {
32489             this.el.removeClass('is-valid');
32490             this.el.addClass('is-invalid');
32491         }
32492         
32493         
32494         this.fireEvent('invalid', this, msg);
32495     }
32496     
32497    
32498 });
32499
32500 Roo.apply(Roo.bootstrap.FieldLabel, {
32501     
32502     groups: {},
32503     
32504      /**
32505     * register a FieldLabel Group
32506     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32507     */
32508     register : function(label)
32509     {
32510         if(this.groups.hasOwnProperty(label.target)){
32511             return;
32512         }
32513      
32514         this.groups[label.target] = label;
32515         
32516     },
32517     /**
32518     * fetch a FieldLabel Group based on the target
32519     * @param {string} target
32520     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32521     */
32522     get: function(target) {
32523         if (typeof(this.groups[target]) == 'undefined') {
32524             return false;
32525         }
32526         
32527         return this.groups[target] ;
32528     }
32529 });
32530
32531  
32532
32533  /*
32534  * - LGPL
32535  *
32536  * page DateSplitField.
32537  * 
32538  */
32539
32540
32541 /**
32542  * @class Roo.bootstrap.DateSplitField
32543  * @extends Roo.bootstrap.Component
32544  * Bootstrap DateSplitField class
32545  * @cfg {string} fieldLabel - the label associated
32546  * @cfg {Number} labelWidth set the width of label (0-12)
32547  * @cfg {String} labelAlign (top|left)
32548  * @cfg {Boolean} dayAllowBlank (true|false) default false
32549  * @cfg {Boolean} monthAllowBlank (true|false) default false
32550  * @cfg {Boolean} yearAllowBlank (true|false) default false
32551  * @cfg {string} dayPlaceholder 
32552  * @cfg {string} monthPlaceholder
32553  * @cfg {string} yearPlaceholder
32554  * @cfg {string} dayFormat default 'd'
32555  * @cfg {string} monthFormat default 'm'
32556  * @cfg {string} yearFormat default 'Y'
32557  * @cfg {Number} labellg set the width of label (1-12)
32558  * @cfg {Number} labelmd set the width of label (1-12)
32559  * @cfg {Number} labelsm set the width of label (1-12)
32560  * @cfg {Number} labelxs set the width of label (1-12)
32561
32562  *     
32563  * @constructor
32564  * Create a new DateSplitField
32565  * @param {Object} config The config object
32566  */
32567
32568 Roo.bootstrap.DateSplitField = function(config){
32569     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32570     
32571     this.addEvents({
32572         // raw events
32573          /**
32574          * @event years
32575          * getting the data of years
32576          * @param {Roo.bootstrap.DateSplitField} this
32577          * @param {Object} years
32578          */
32579         "years" : true,
32580         /**
32581          * @event days
32582          * getting the data of days
32583          * @param {Roo.bootstrap.DateSplitField} this
32584          * @param {Object} days
32585          */
32586         "days" : true,
32587         /**
32588          * @event invalid
32589          * Fires after the field has been marked as invalid.
32590          * @param {Roo.form.Field} this
32591          * @param {String} msg The validation message
32592          */
32593         invalid : true,
32594        /**
32595          * @event valid
32596          * Fires after the field has been validated with no errors.
32597          * @param {Roo.form.Field} this
32598          */
32599         valid : true
32600     });
32601 };
32602
32603 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32604     
32605     fieldLabel : '',
32606     labelAlign : 'top',
32607     labelWidth : 3,
32608     dayAllowBlank : false,
32609     monthAllowBlank : false,
32610     yearAllowBlank : false,
32611     dayPlaceholder : '',
32612     monthPlaceholder : '',
32613     yearPlaceholder : '',
32614     dayFormat : 'd',
32615     monthFormat : 'm',
32616     yearFormat : 'Y',
32617     isFormField : true,
32618     labellg : 0,
32619     labelmd : 0,
32620     labelsm : 0,
32621     labelxs : 0,
32622     
32623     getAutoCreate : function()
32624     {
32625         var cfg = {
32626             tag : 'div',
32627             cls : 'row roo-date-split-field-group',
32628             cn : [
32629                 {
32630                     tag : 'input',
32631                     type : 'hidden',
32632                     cls : 'form-hidden-field roo-date-split-field-group-value',
32633                     name : this.name
32634                 }
32635             ]
32636         };
32637         
32638         var labelCls = 'col-md-12';
32639         var contentCls = 'col-md-4';
32640         
32641         if(this.fieldLabel){
32642             
32643             var label = {
32644                 tag : 'div',
32645                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32646                 cn : [
32647                     {
32648                         tag : 'label',
32649                         html : this.fieldLabel
32650                     }
32651                 ]
32652             };
32653             
32654             if(this.labelAlign == 'left'){
32655             
32656                 if(this.labelWidth > 12){
32657                     label.style = "width: " + this.labelWidth + 'px';
32658                 }
32659
32660                 if(this.labelWidth < 13 && this.labelmd == 0){
32661                     this.labelmd = this.labelWidth;
32662                 }
32663
32664                 if(this.labellg > 0){
32665                     labelCls = ' col-lg-' + this.labellg;
32666                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32667                 }
32668
32669                 if(this.labelmd > 0){
32670                     labelCls = ' col-md-' + this.labelmd;
32671                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32672                 }
32673
32674                 if(this.labelsm > 0){
32675                     labelCls = ' col-sm-' + this.labelsm;
32676                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32677                 }
32678
32679                 if(this.labelxs > 0){
32680                     labelCls = ' col-xs-' + this.labelxs;
32681                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32682                 }
32683             }
32684             
32685             label.cls += ' ' + labelCls;
32686             
32687             cfg.cn.push(label);
32688         }
32689         
32690         Roo.each(['day', 'month', 'year'], function(t){
32691             cfg.cn.push({
32692                 tag : 'div',
32693                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32694             });
32695         }, this);
32696         
32697         return cfg;
32698     },
32699     
32700     inputEl: function ()
32701     {
32702         return this.el.select('.roo-date-split-field-group-value', true).first();
32703     },
32704     
32705     onRender : function(ct, position) 
32706     {
32707         var _this = this;
32708         
32709         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32710         
32711         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32712         
32713         this.dayField = new Roo.bootstrap.ComboBox({
32714             allowBlank : this.dayAllowBlank,
32715             alwaysQuery : true,
32716             displayField : 'value',
32717             editable : false,
32718             fieldLabel : '',
32719             forceSelection : true,
32720             mode : 'local',
32721             placeholder : this.dayPlaceholder,
32722             selectOnFocus : true,
32723             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32724             triggerAction : 'all',
32725             typeAhead : true,
32726             valueField : 'value',
32727             store : new Roo.data.SimpleStore({
32728                 data : (function() {    
32729                     var days = [];
32730                     _this.fireEvent('days', _this, days);
32731                     return days;
32732                 })(),
32733                 fields : [ 'value' ]
32734             }),
32735             listeners : {
32736                 select : function (_self, record, index)
32737                 {
32738                     _this.setValue(_this.getValue());
32739                 }
32740             }
32741         });
32742
32743         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32744         
32745         this.monthField = new Roo.bootstrap.MonthField({
32746             after : '<i class=\"fa fa-calendar\"></i>',
32747             allowBlank : this.monthAllowBlank,
32748             placeholder : this.monthPlaceholder,
32749             readOnly : true,
32750             listeners : {
32751                 render : function (_self)
32752                 {
32753                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32754                         e.preventDefault();
32755                         _self.focus();
32756                     });
32757                 },
32758                 select : function (_self, oldvalue, newvalue)
32759                 {
32760                     _this.setValue(_this.getValue());
32761                 }
32762             }
32763         });
32764         
32765         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32766         
32767         this.yearField = new Roo.bootstrap.ComboBox({
32768             allowBlank : this.yearAllowBlank,
32769             alwaysQuery : true,
32770             displayField : 'value',
32771             editable : false,
32772             fieldLabel : '',
32773             forceSelection : true,
32774             mode : 'local',
32775             placeholder : this.yearPlaceholder,
32776             selectOnFocus : true,
32777             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32778             triggerAction : 'all',
32779             typeAhead : true,
32780             valueField : 'value',
32781             store : new Roo.data.SimpleStore({
32782                 data : (function() {
32783                     var years = [];
32784                     _this.fireEvent('years', _this, years);
32785                     return years;
32786                 })(),
32787                 fields : [ 'value' ]
32788             }),
32789             listeners : {
32790                 select : function (_self, record, index)
32791                 {
32792                     _this.setValue(_this.getValue());
32793                 }
32794             }
32795         });
32796
32797         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32798     },
32799     
32800     setValue : function(v, format)
32801     {
32802         this.inputEl.dom.value = v;
32803         
32804         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32805         
32806         var d = Date.parseDate(v, f);
32807         
32808         if(!d){
32809             this.validate();
32810             return;
32811         }
32812         
32813         this.setDay(d.format(this.dayFormat));
32814         this.setMonth(d.format(this.monthFormat));
32815         this.setYear(d.format(this.yearFormat));
32816         
32817         this.validate();
32818         
32819         return;
32820     },
32821     
32822     setDay : function(v)
32823     {
32824         this.dayField.setValue(v);
32825         this.inputEl.dom.value = this.getValue();
32826         this.validate();
32827         return;
32828     },
32829     
32830     setMonth : function(v)
32831     {
32832         this.monthField.setValue(v, true);
32833         this.inputEl.dom.value = this.getValue();
32834         this.validate();
32835         return;
32836     },
32837     
32838     setYear : function(v)
32839     {
32840         this.yearField.setValue(v);
32841         this.inputEl.dom.value = this.getValue();
32842         this.validate();
32843         return;
32844     },
32845     
32846     getDay : function()
32847     {
32848         return this.dayField.getValue();
32849     },
32850     
32851     getMonth : function()
32852     {
32853         return this.monthField.getValue();
32854     },
32855     
32856     getYear : function()
32857     {
32858         return this.yearField.getValue();
32859     },
32860     
32861     getValue : function()
32862     {
32863         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32864         
32865         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32866         
32867         return date;
32868     },
32869     
32870     reset : function()
32871     {
32872         this.setDay('');
32873         this.setMonth('');
32874         this.setYear('');
32875         this.inputEl.dom.value = '';
32876         this.validate();
32877         return;
32878     },
32879     
32880     validate : function()
32881     {
32882         var d = this.dayField.validate();
32883         var m = this.monthField.validate();
32884         var y = this.yearField.validate();
32885         
32886         var valid = true;
32887         
32888         if(
32889                 (!this.dayAllowBlank && !d) ||
32890                 (!this.monthAllowBlank && !m) ||
32891                 (!this.yearAllowBlank && !y)
32892         ){
32893             valid = false;
32894         }
32895         
32896         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32897             return valid;
32898         }
32899         
32900         if(valid){
32901             this.markValid();
32902             return valid;
32903         }
32904         
32905         this.markInvalid();
32906         
32907         return valid;
32908     },
32909     
32910     markValid : function()
32911     {
32912         
32913         var label = this.el.select('label', true).first();
32914         var icon = this.el.select('i.fa-star', true).first();
32915
32916         if(label && icon){
32917             icon.remove();
32918         }
32919         
32920         this.fireEvent('valid', this);
32921     },
32922     
32923      /**
32924      * Mark this field as invalid
32925      * @param {String} msg The validation message
32926      */
32927     markInvalid : function(msg)
32928     {
32929         
32930         var label = this.el.select('label', true).first();
32931         var icon = this.el.select('i.fa-star', true).first();
32932
32933         if(label && !icon){
32934             this.el.select('.roo-date-split-field-label', true).createChild({
32935                 tag : 'i',
32936                 cls : 'text-danger fa fa-lg fa-star',
32937                 tooltip : 'This field is required',
32938                 style : 'margin-right:5px;'
32939             }, label, true);
32940         }
32941         
32942         this.fireEvent('invalid', this, msg);
32943     },
32944     
32945     clearInvalid : function()
32946     {
32947         var label = this.el.select('label', true).first();
32948         var icon = this.el.select('i.fa-star', true).first();
32949
32950         if(label && icon){
32951             icon.remove();
32952         }
32953         
32954         this.fireEvent('valid', this);
32955     },
32956     
32957     getName: function()
32958     {
32959         return this.name;
32960     }
32961     
32962 });
32963
32964  /**
32965  *
32966  * This is based on 
32967  * http://masonry.desandro.com
32968  *
32969  * The idea is to render all the bricks based on vertical width...
32970  *
32971  * The original code extends 'outlayer' - we might need to use that....
32972  * 
32973  */
32974
32975
32976 /**
32977  * @class Roo.bootstrap.LayoutMasonry
32978  * @extends Roo.bootstrap.Component
32979  * Bootstrap Layout Masonry class
32980  * 
32981  * @constructor
32982  * Create a new Element
32983  * @param {Object} config The config object
32984  */
32985
32986 Roo.bootstrap.LayoutMasonry = function(config){
32987     
32988     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32989     
32990     this.bricks = [];
32991     
32992     Roo.bootstrap.LayoutMasonry.register(this);
32993     
32994     this.addEvents({
32995         // raw events
32996         /**
32997          * @event layout
32998          * Fire after layout the items
32999          * @param {Roo.bootstrap.LayoutMasonry} this
33000          * @param {Roo.EventObject} e
33001          */
33002         "layout" : true
33003     });
33004     
33005 };
33006
33007 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33008     
33009     /**
33010      * @cfg {Boolean} isLayoutInstant = no animation?
33011      */   
33012     isLayoutInstant : false, // needed?
33013    
33014     /**
33015      * @cfg {Number} boxWidth  width of the columns
33016      */   
33017     boxWidth : 450,
33018     
33019       /**
33020      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33021      */   
33022     boxHeight : 0,
33023     
33024     /**
33025      * @cfg {Number} padWidth padding below box..
33026      */   
33027     padWidth : 10, 
33028     
33029     /**
33030      * @cfg {Number} gutter gutter width..
33031      */   
33032     gutter : 10,
33033     
33034      /**
33035      * @cfg {Number} maxCols maximum number of columns
33036      */   
33037     
33038     maxCols: 0,
33039     
33040     /**
33041      * @cfg {Boolean} isAutoInitial defalut true
33042      */   
33043     isAutoInitial : true, 
33044     
33045     containerWidth: 0,
33046     
33047     /**
33048      * @cfg {Boolean} isHorizontal defalut false
33049      */   
33050     isHorizontal : false, 
33051
33052     currentSize : null,
33053     
33054     tag: 'div',
33055     
33056     cls: '',
33057     
33058     bricks: null, //CompositeElement
33059     
33060     cols : 1,
33061     
33062     _isLayoutInited : false,
33063     
33064 //    isAlternative : false, // only use for vertical layout...
33065     
33066     /**
33067      * @cfg {Number} alternativePadWidth padding below box..
33068      */   
33069     alternativePadWidth : 50,
33070     
33071     selectedBrick : [],
33072     
33073     getAutoCreate : function(){
33074         
33075         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33076         
33077         var cfg = {
33078             tag: this.tag,
33079             cls: 'blog-masonary-wrapper ' + this.cls,
33080             cn : {
33081                 cls : 'mas-boxes masonary'
33082             }
33083         };
33084         
33085         return cfg;
33086     },
33087     
33088     getChildContainer: function( )
33089     {
33090         if (this.boxesEl) {
33091             return this.boxesEl;
33092         }
33093         
33094         this.boxesEl = this.el.select('.mas-boxes').first();
33095         
33096         return this.boxesEl;
33097     },
33098     
33099     
33100     initEvents : function()
33101     {
33102         var _this = this;
33103         
33104         if(this.isAutoInitial){
33105             Roo.log('hook children rendered');
33106             this.on('childrenrendered', function() {
33107                 Roo.log('children rendered');
33108                 _this.initial();
33109             } ,this);
33110         }
33111     },
33112     
33113     initial : function()
33114     {
33115         this.selectedBrick = [];
33116         
33117         this.currentSize = this.el.getBox(true);
33118         
33119         Roo.EventManager.onWindowResize(this.resize, this); 
33120
33121         if(!this.isAutoInitial){
33122             this.layout();
33123             return;
33124         }
33125         
33126         this.layout();
33127         
33128         return;
33129         //this.layout.defer(500,this);
33130         
33131     },
33132     
33133     resize : function()
33134     {
33135         var cs = this.el.getBox(true);
33136         
33137         if (
33138                 this.currentSize.width == cs.width && 
33139                 this.currentSize.x == cs.x && 
33140                 this.currentSize.height == cs.height && 
33141                 this.currentSize.y == cs.y 
33142         ) {
33143             Roo.log("no change in with or X or Y");
33144             return;
33145         }
33146         
33147         this.currentSize = cs;
33148         
33149         this.layout();
33150         
33151     },
33152     
33153     layout : function()
33154     {   
33155         this._resetLayout();
33156         
33157         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33158         
33159         this.layoutItems( isInstant );
33160       
33161         this._isLayoutInited = true;
33162         
33163         this.fireEvent('layout', this);
33164         
33165     },
33166     
33167     _resetLayout : function()
33168     {
33169         if(this.isHorizontal){
33170             this.horizontalMeasureColumns();
33171             return;
33172         }
33173         
33174         this.verticalMeasureColumns();
33175         
33176     },
33177     
33178     verticalMeasureColumns : function()
33179     {
33180         this.getContainerWidth();
33181         
33182 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33183 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33184 //            return;
33185 //        }
33186         
33187         var boxWidth = this.boxWidth + this.padWidth;
33188         
33189         if(this.containerWidth < this.boxWidth){
33190             boxWidth = this.containerWidth
33191         }
33192         
33193         var containerWidth = this.containerWidth;
33194         
33195         var cols = Math.floor(containerWidth / boxWidth);
33196         
33197         this.cols = Math.max( cols, 1 );
33198         
33199         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33200         
33201         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33202         
33203         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33204         
33205         this.colWidth = boxWidth + avail - this.padWidth;
33206         
33207         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33208         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33209     },
33210     
33211     horizontalMeasureColumns : function()
33212     {
33213         this.getContainerWidth();
33214         
33215         var boxWidth = this.boxWidth;
33216         
33217         if(this.containerWidth < boxWidth){
33218             boxWidth = this.containerWidth;
33219         }
33220         
33221         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33222         
33223         this.el.setHeight(boxWidth);
33224         
33225     },
33226     
33227     getContainerWidth : function()
33228     {
33229         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33230     },
33231     
33232     layoutItems : function( isInstant )
33233     {
33234         Roo.log(this.bricks);
33235         
33236         var items = Roo.apply([], this.bricks);
33237         
33238         if(this.isHorizontal){
33239             this._horizontalLayoutItems( items , isInstant );
33240             return;
33241         }
33242         
33243 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33244 //            this._verticalAlternativeLayoutItems( items , isInstant );
33245 //            return;
33246 //        }
33247         
33248         this._verticalLayoutItems( items , isInstant );
33249         
33250     },
33251     
33252     _verticalLayoutItems : function ( items , isInstant)
33253     {
33254         if ( !items || !items.length ) {
33255             return;
33256         }
33257         
33258         var standard = [
33259             ['xs', 'xs', 'xs', 'tall'],
33260             ['xs', 'xs', 'tall'],
33261             ['xs', 'xs', 'sm'],
33262             ['xs', 'xs', 'xs'],
33263             ['xs', 'tall'],
33264             ['xs', 'sm'],
33265             ['xs', 'xs'],
33266             ['xs'],
33267             
33268             ['sm', 'xs', 'xs'],
33269             ['sm', 'xs'],
33270             ['sm'],
33271             
33272             ['tall', 'xs', 'xs', 'xs'],
33273             ['tall', 'xs', 'xs'],
33274             ['tall', 'xs'],
33275             ['tall']
33276             
33277         ];
33278         
33279         var queue = [];
33280         
33281         var boxes = [];
33282         
33283         var box = [];
33284         
33285         Roo.each(items, function(item, k){
33286             
33287             switch (item.size) {
33288                 // these layouts take up a full box,
33289                 case 'md' :
33290                 case 'md-left' :
33291                 case 'md-right' :
33292                 case 'wide' :
33293                     
33294                     if(box.length){
33295                         boxes.push(box);
33296                         box = [];
33297                     }
33298                     
33299                     boxes.push([item]);
33300                     
33301                     break;
33302                     
33303                 case 'xs' :
33304                 case 'sm' :
33305                 case 'tall' :
33306                     
33307                     box.push(item);
33308                     
33309                     break;
33310                 default :
33311                     break;
33312                     
33313             }
33314             
33315         }, this);
33316         
33317         if(box.length){
33318             boxes.push(box);
33319             box = [];
33320         }
33321         
33322         var filterPattern = function(box, length)
33323         {
33324             if(!box.length){
33325                 return;
33326             }
33327             
33328             var match = false;
33329             
33330             var pattern = box.slice(0, length);
33331             
33332             var format = [];
33333             
33334             Roo.each(pattern, function(i){
33335                 format.push(i.size);
33336             }, this);
33337             
33338             Roo.each(standard, function(s){
33339                 
33340                 if(String(s) != String(format)){
33341                     return;
33342                 }
33343                 
33344                 match = true;
33345                 return false;
33346                 
33347             }, this);
33348             
33349             if(!match && length == 1){
33350                 return;
33351             }
33352             
33353             if(!match){
33354                 filterPattern(box, length - 1);
33355                 return;
33356             }
33357                 
33358             queue.push(pattern);
33359
33360             box = box.slice(length, box.length);
33361
33362             filterPattern(box, 4);
33363
33364             return;
33365             
33366         }
33367         
33368         Roo.each(boxes, function(box, k){
33369             
33370             if(!box.length){
33371                 return;
33372             }
33373             
33374             if(box.length == 1){
33375                 queue.push(box);
33376                 return;
33377             }
33378             
33379             filterPattern(box, 4);
33380             
33381         }, this);
33382         
33383         this._processVerticalLayoutQueue( queue, isInstant );
33384         
33385     },
33386     
33387 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33388 //    {
33389 //        if ( !items || !items.length ) {
33390 //            return;
33391 //        }
33392 //
33393 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33394 //        
33395 //    },
33396     
33397     _horizontalLayoutItems : function ( items , isInstant)
33398     {
33399         if ( !items || !items.length || items.length < 3) {
33400             return;
33401         }
33402         
33403         items.reverse();
33404         
33405         var eItems = items.slice(0, 3);
33406         
33407         items = items.slice(3, items.length);
33408         
33409         var standard = [
33410             ['xs', 'xs', 'xs', 'wide'],
33411             ['xs', 'xs', 'wide'],
33412             ['xs', 'xs', 'sm'],
33413             ['xs', 'xs', 'xs'],
33414             ['xs', 'wide'],
33415             ['xs', 'sm'],
33416             ['xs', 'xs'],
33417             ['xs'],
33418             
33419             ['sm', 'xs', 'xs'],
33420             ['sm', 'xs'],
33421             ['sm'],
33422             
33423             ['wide', 'xs', 'xs', 'xs'],
33424             ['wide', 'xs', 'xs'],
33425             ['wide', 'xs'],
33426             ['wide'],
33427             
33428             ['wide-thin']
33429         ];
33430         
33431         var queue = [];
33432         
33433         var boxes = [];
33434         
33435         var box = [];
33436         
33437         Roo.each(items, function(item, k){
33438             
33439             switch (item.size) {
33440                 case 'md' :
33441                 case 'md-left' :
33442                 case 'md-right' :
33443                 case 'tall' :
33444                     
33445                     if(box.length){
33446                         boxes.push(box);
33447                         box = [];
33448                     }
33449                     
33450                     boxes.push([item]);
33451                     
33452                     break;
33453                     
33454                 case 'xs' :
33455                 case 'sm' :
33456                 case 'wide' :
33457                 case 'wide-thin' :
33458                     
33459                     box.push(item);
33460                     
33461                     break;
33462                 default :
33463                     break;
33464                     
33465             }
33466             
33467         }, this);
33468         
33469         if(box.length){
33470             boxes.push(box);
33471             box = [];
33472         }
33473         
33474         var filterPattern = function(box, length)
33475         {
33476             if(!box.length){
33477                 return;
33478             }
33479             
33480             var match = false;
33481             
33482             var pattern = box.slice(0, length);
33483             
33484             var format = [];
33485             
33486             Roo.each(pattern, function(i){
33487                 format.push(i.size);
33488             }, this);
33489             
33490             Roo.each(standard, function(s){
33491                 
33492                 if(String(s) != String(format)){
33493                     return;
33494                 }
33495                 
33496                 match = true;
33497                 return false;
33498                 
33499             }, this);
33500             
33501             if(!match && length == 1){
33502                 return;
33503             }
33504             
33505             if(!match){
33506                 filterPattern(box, length - 1);
33507                 return;
33508             }
33509                 
33510             queue.push(pattern);
33511
33512             box = box.slice(length, box.length);
33513
33514             filterPattern(box, 4);
33515
33516             return;
33517             
33518         }
33519         
33520         Roo.each(boxes, function(box, k){
33521             
33522             if(!box.length){
33523                 return;
33524             }
33525             
33526             if(box.length == 1){
33527                 queue.push(box);
33528                 return;
33529             }
33530             
33531             filterPattern(box, 4);
33532             
33533         }, this);
33534         
33535         
33536         var prune = [];
33537         
33538         var pos = this.el.getBox(true);
33539         
33540         var minX = pos.x;
33541         
33542         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33543         
33544         var hit_end = false;
33545         
33546         Roo.each(queue, function(box){
33547             
33548             if(hit_end){
33549                 
33550                 Roo.each(box, function(b){
33551                 
33552                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33553                     b.el.hide();
33554
33555                 }, this);
33556
33557                 return;
33558             }
33559             
33560             var mx = 0;
33561             
33562             Roo.each(box, function(b){
33563                 
33564                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33565                 b.el.show();
33566
33567                 mx = Math.max(mx, b.x);
33568                 
33569             }, this);
33570             
33571             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33572             
33573             if(maxX < minX){
33574                 
33575                 Roo.each(box, function(b){
33576                 
33577                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33578                     b.el.hide();
33579                     
33580                 }, this);
33581                 
33582                 hit_end = true;
33583                 
33584                 return;
33585             }
33586             
33587             prune.push(box);
33588             
33589         }, this);
33590         
33591         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33592     },
33593     
33594     /** Sets position of item in DOM
33595     * @param {Element} item
33596     * @param {Number} x - horizontal position
33597     * @param {Number} y - vertical position
33598     * @param {Boolean} isInstant - disables transitions
33599     */
33600     _processVerticalLayoutQueue : function( queue, isInstant )
33601     {
33602         var pos = this.el.getBox(true);
33603         var x = pos.x;
33604         var y = pos.y;
33605         var maxY = [];
33606         
33607         for (var i = 0; i < this.cols; i++){
33608             maxY[i] = pos.y;
33609         }
33610         
33611         Roo.each(queue, function(box, k){
33612             
33613             var col = k % this.cols;
33614             
33615             Roo.each(box, function(b,kk){
33616                 
33617                 b.el.position('absolute');
33618                 
33619                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33620                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33621                 
33622                 if(b.size == 'md-left' || b.size == 'md-right'){
33623                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33624                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33625                 }
33626                 
33627                 b.el.setWidth(width);
33628                 b.el.setHeight(height);
33629                 // iframe?
33630                 b.el.select('iframe',true).setSize(width,height);
33631                 
33632             }, this);
33633             
33634             for (var i = 0; i < this.cols; i++){
33635                 
33636                 if(maxY[i] < maxY[col]){
33637                     col = i;
33638                     continue;
33639                 }
33640                 
33641                 col = Math.min(col, i);
33642                 
33643             }
33644             
33645             x = pos.x + col * (this.colWidth + this.padWidth);
33646             
33647             y = maxY[col];
33648             
33649             var positions = [];
33650             
33651             switch (box.length){
33652                 case 1 :
33653                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33654                     break;
33655                 case 2 :
33656                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33657                     break;
33658                 case 3 :
33659                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33660                     break;
33661                 case 4 :
33662                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33663                     break;
33664                 default :
33665                     break;
33666             }
33667             
33668             Roo.each(box, function(b,kk){
33669                 
33670                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33671                 
33672                 var sz = b.el.getSize();
33673                 
33674                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33675                 
33676             }, this);
33677             
33678         }, this);
33679         
33680         var mY = 0;
33681         
33682         for (var i = 0; i < this.cols; i++){
33683             mY = Math.max(mY, maxY[i]);
33684         }
33685         
33686         this.el.setHeight(mY - pos.y);
33687         
33688     },
33689     
33690 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33691 //    {
33692 //        var pos = this.el.getBox(true);
33693 //        var x = pos.x;
33694 //        var y = pos.y;
33695 //        var maxX = pos.right;
33696 //        
33697 //        var maxHeight = 0;
33698 //        
33699 //        Roo.each(items, function(item, k){
33700 //            
33701 //            var c = k % 2;
33702 //            
33703 //            item.el.position('absolute');
33704 //                
33705 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33706 //
33707 //            item.el.setWidth(width);
33708 //
33709 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33710 //
33711 //            item.el.setHeight(height);
33712 //            
33713 //            if(c == 0){
33714 //                item.el.setXY([x, y], isInstant ? false : true);
33715 //            } else {
33716 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33717 //            }
33718 //            
33719 //            y = y + height + this.alternativePadWidth;
33720 //            
33721 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33722 //            
33723 //        }, this);
33724 //        
33725 //        this.el.setHeight(maxHeight);
33726 //        
33727 //    },
33728     
33729     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33730     {
33731         var pos = this.el.getBox(true);
33732         
33733         var minX = pos.x;
33734         var minY = pos.y;
33735         
33736         var maxX = pos.right;
33737         
33738         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33739         
33740         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33741         
33742         Roo.each(queue, function(box, k){
33743             
33744             Roo.each(box, function(b, kk){
33745                 
33746                 b.el.position('absolute');
33747                 
33748                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33749                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33750                 
33751                 if(b.size == 'md-left' || b.size == 'md-right'){
33752                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33753                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33754                 }
33755                 
33756                 b.el.setWidth(width);
33757                 b.el.setHeight(height);
33758                 
33759             }, this);
33760             
33761             if(!box.length){
33762                 return;
33763             }
33764             
33765             var positions = [];
33766             
33767             switch (box.length){
33768                 case 1 :
33769                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33770                     break;
33771                 case 2 :
33772                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33773                     break;
33774                 case 3 :
33775                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33776                     break;
33777                 case 4 :
33778                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33779                     break;
33780                 default :
33781                     break;
33782             }
33783             
33784             Roo.each(box, function(b,kk){
33785                 
33786                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33787                 
33788                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33789                 
33790             }, this);
33791             
33792         }, this);
33793         
33794     },
33795     
33796     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33797     {
33798         Roo.each(eItems, function(b,k){
33799             
33800             b.size = (k == 0) ? 'sm' : 'xs';
33801             b.x = (k == 0) ? 2 : 1;
33802             b.y = (k == 0) ? 2 : 1;
33803             
33804             b.el.position('absolute');
33805             
33806             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33807                 
33808             b.el.setWidth(width);
33809             
33810             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33811             
33812             b.el.setHeight(height);
33813             
33814         }, this);
33815
33816         var positions = [];
33817         
33818         positions.push({
33819             x : maxX - this.unitWidth * 2 - this.gutter,
33820             y : minY
33821         });
33822         
33823         positions.push({
33824             x : maxX - this.unitWidth,
33825             y : minY + (this.unitWidth + this.gutter) * 2
33826         });
33827         
33828         positions.push({
33829             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33830             y : minY
33831         });
33832         
33833         Roo.each(eItems, function(b,k){
33834             
33835             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33836
33837         }, this);
33838         
33839     },
33840     
33841     getVerticalOneBoxColPositions : function(x, y, box)
33842     {
33843         var pos = [];
33844         
33845         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33846         
33847         if(box[0].size == 'md-left'){
33848             rand = 0;
33849         }
33850         
33851         if(box[0].size == 'md-right'){
33852             rand = 1;
33853         }
33854         
33855         pos.push({
33856             x : x + (this.unitWidth + this.gutter) * rand,
33857             y : y
33858         });
33859         
33860         return pos;
33861     },
33862     
33863     getVerticalTwoBoxColPositions : function(x, y, box)
33864     {
33865         var pos = [];
33866         
33867         if(box[0].size == 'xs'){
33868             
33869             pos.push({
33870                 x : x,
33871                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33872             });
33873
33874             pos.push({
33875                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33876                 y : y
33877             });
33878             
33879             return pos;
33880             
33881         }
33882         
33883         pos.push({
33884             x : x,
33885             y : y
33886         });
33887
33888         pos.push({
33889             x : x + (this.unitWidth + this.gutter) * 2,
33890             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33891         });
33892         
33893         return pos;
33894         
33895     },
33896     
33897     getVerticalThreeBoxColPositions : function(x, y, box)
33898     {
33899         var pos = [];
33900         
33901         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33902             
33903             pos.push({
33904                 x : x,
33905                 y : y
33906             });
33907
33908             pos.push({
33909                 x : x + (this.unitWidth + this.gutter) * 1,
33910                 y : y
33911             });
33912             
33913             pos.push({
33914                 x : x + (this.unitWidth + this.gutter) * 2,
33915                 y : y
33916             });
33917             
33918             return pos;
33919             
33920         }
33921         
33922         if(box[0].size == 'xs' && box[1].size == 'xs'){
33923             
33924             pos.push({
33925                 x : x,
33926                 y : y
33927             });
33928
33929             pos.push({
33930                 x : x,
33931                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33932             });
33933             
33934             pos.push({
33935                 x : x + (this.unitWidth + this.gutter) * 1,
33936                 y : y
33937             });
33938             
33939             return pos;
33940             
33941         }
33942         
33943         pos.push({
33944             x : x,
33945             y : y
33946         });
33947
33948         pos.push({
33949             x : x + (this.unitWidth + this.gutter) * 2,
33950             y : y
33951         });
33952
33953         pos.push({
33954             x : x + (this.unitWidth + this.gutter) * 2,
33955             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33956         });
33957             
33958         return pos;
33959         
33960     },
33961     
33962     getVerticalFourBoxColPositions : function(x, y, box)
33963     {
33964         var pos = [];
33965         
33966         if(box[0].size == 'xs'){
33967             
33968             pos.push({
33969                 x : x,
33970                 y : y
33971             });
33972
33973             pos.push({
33974                 x : x,
33975                 y : y + (this.unitHeight + this.gutter) * 1
33976             });
33977             
33978             pos.push({
33979                 x : x,
33980                 y : y + (this.unitHeight + this.gutter) * 2
33981             });
33982             
33983             pos.push({
33984                 x : x + (this.unitWidth + this.gutter) * 1,
33985                 y : y
33986             });
33987             
33988             return pos;
33989             
33990         }
33991         
33992         pos.push({
33993             x : x,
33994             y : y
33995         });
33996
33997         pos.push({
33998             x : x + (this.unitWidth + this.gutter) * 2,
33999             y : y
34000         });
34001
34002         pos.push({
34003             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34004             y : y + (this.unitHeight + this.gutter) * 1
34005         });
34006
34007         pos.push({
34008             x : x + (this.unitWidth + this.gutter) * 2,
34009             y : y + (this.unitWidth + this.gutter) * 2
34010         });
34011
34012         return pos;
34013         
34014     },
34015     
34016     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34017     {
34018         var pos = [];
34019         
34020         if(box[0].size == 'md-left'){
34021             pos.push({
34022                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34023                 y : minY
34024             });
34025             
34026             return pos;
34027         }
34028         
34029         if(box[0].size == 'md-right'){
34030             pos.push({
34031                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34032                 y : minY + (this.unitWidth + this.gutter) * 1
34033             });
34034             
34035             return pos;
34036         }
34037         
34038         var rand = Math.floor(Math.random() * (4 - box[0].y));
34039         
34040         pos.push({
34041             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34042             y : minY + (this.unitWidth + this.gutter) * rand
34043         });
34044         
34045         return pos;
34046         
34047     },
34048     
34049     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34050     {
34051         var pos = [];
34052         
34053         if(box[0].size == 'xs'){
34054             
34055             pos.push({
34056                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34057                 y : minY
34058             });
34059
34060             pos.push({
34061                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34062                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34063             });
34064             
34065             return pos;
34066             
34067         }
34068         
34069         pos.push({
34070             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34071             y : minY
34072         });
34073
34074         pos.push({
34075             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34076             y : minY + (this.unitWidth + this.gutter) * 2
34077         });
34078         
34079         return pos;
34080         
34081     },
34082     
34083     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34084     {
34085         var pos = [];
34086         
34087         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34088             
34089             pos.push({
34090                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34091                 y : minY
34092             });
34093
34094             pos.push({
34095                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34096                 y : minY + (this.unitWidth + this.gutter) * 1
34097             });
34098             
34099             pos.push({
34100                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34101                 y : minY + (this.unitWidth + this.gutter) * 2
34102             });
34103             
34104             return pos;
34105             
34106         }
34107         
34108         if(box[0].size == 'xs' && box[1].size == 'xs'){
34109             
34110             pos.push({
34111                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34112                 y : minY
34113             });
34114
34115             pos.push({
34116                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34117                 y : minY
34118             });
34119             
34120             pos.push({
34121                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34122                 y : minY + (this.unitWidth + this.gutter) * 1
34123             });
34124             
34125             return pos;
34126             
34127         }
34128         
34129         pos.push({
34130             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34131             y : minY
34132         });
34133
34134         pos.push({
34135             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34136             y : minY + (this.unitWidth + this.gutter) * 2
34137         });
34138
34139         pos.push({
34140             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34141             y : minY + (this.unitWidth + this.gutter) * 2
34142         });
34143             
34144         return pos;
34145         
34146     },
34147     
34148     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34149     {
34150         var pos = [];
34151         
34152         if(box[0].size == 'xs'){
34153             
34154             pos.push({
34155                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34156                 y : minY
34157             });
34158
34159             pos.push({
34160                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34161                 y : minY
34162             });
34163             
34164             pos.push({
34165                 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),
34166                 y : minY
34167             });
34168             
34169             pos.push({
34170                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34171                 y : minY + (this.unitWidth + this.gutter) * 1
34172             });
34173             
34174             return pos;
34175             
34176         }
34177         
34178         pos.push({
34179             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34180             y : minY
34181         });
34182         
34183         pos.push({
34184             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34185             y : minY + (this.unitWidth + this.gutter) * 2
34186         });
34187         
34188         pos.push({
34189             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34190             y : minY + (this.unitWidth + this.gutter) * 2
34191         });
34192         
34193         pos.push({
34194             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),
34195             y : minY + (this.unitWidth + this.gutter) * 2
34196         });
34197
34198         return pos;
34199         
34200     },
34201     
34202     /**
34203     * remove a Masonry Brick
34204     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34205     */
34206     removeBrick : function(brick_id)
34207     {
34208         if (!brick_id) {
34209             return;
34210         }
34211         
34212         for (var i = 0; i<this.bricks.length; i++) {
34213             if (this.bricks[i].id == brick_id) {
34214                 this.bricks.splice(i,1);
34215                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34216                 this.initial();
34217             }
34218         }
34219     },
34220     
34221     /**
34222     * adds a Masonry Brick
34223     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34224     */
34225     addBrick : function(cfg)
34226     {
34227         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34228         //this.register(cn);
34229         cn.parentId = this.id;
34230         cn.render(this.el);
34231         return cn;
34232     },
34233     
34234     /**
34235     * register a Masonry Brick
34236     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34237     */
34238     
34239     register : function(brick)
34240     {
34241         this.bricks.push(brick);
34242         brick.masonryId = this.id;
34243     },
34244     
34245     /**
34246     * clear all the Masonry Brick
34247     */
34248     clearAll : function()
34249     {
34250         this.bricks = [];
34251         //this.getChildContainer().dom.innerHTML = "";
34252         this.el.dom.innerHTML = '';
34253     },
34254     
34255     getSelected : function()
34256     {
34257         if (!this.selectedBrick) {
34258             return false;
34259         }
34260         
34261         return this.selectedBrick;
34262     }
34263 });
34264
34265 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34266     
34267     groups: {},
34268      /**
34269     * register a Masonry Layout
34270     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34271     */
34272     
34273     register : function(layout)
34274     {
34275         this.groups[layout.id] = layout;
34276     },
34277     /**
34278     * fetch a  Masonry Layout based on the masonry layout ID
34279     * @param {string} the masonry layout to add
34280     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34281     */
34282     
34283     get: function(layout_id) {
34284         if (typeof(this.groups[layout_id]) == 'undefined') {
34285             return false;
34286         }
34287         return this.groups[layout_id] ;
34288     }
34289     
34290     
34291     
34292 });
34293
34294  
34295
34296  /**
34297  *
34298  * This is based on 
34299  * http://masonry.desandro.com
34300  *
34301  * The idea is to render all the bricks based on vertical width...
34302  *
34303  * The original code extends 'outlayer' - we might need to use that....
34304  * 
34305  */
34306
34307
34308 /**
34309  * @class Roo.bootstrap.LayoutMasonryAuto
34310  * @extends Roo.bootstrap.Component
34311  * Bootstrap Layout Masonry class
34312  * 
34313  * @constructor
34314  * Create a new Element
34315  * @param {Object} config The config object
34316  */
34317
34318 Roo.bootstrap.LayoutMasonryAuto = function(config){
34319     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34320 };
34321
34322 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34323     
34324       /**
34325      * @cfg {Boolean} isFitWidth  - resize the width..
34326      */   
34327     isFitWidth : false,  // options..
34328     /**
34329      * @cfg {Boolean} isOriginLeft = left align?
34330      */   
34331     isOriginLeft : true,
34332     /**
34333      * @cfg {Boolean} isOriginTop = top align?
34334      */   
34335     isOriginTop : false,
34336     /**
34337      * @cfg {Boolean} isLayoutInstant = no animation?
34338      */   
34339     isLayoutInstant : false, // needed?
34340     /**
34341      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34342      */   
34343     isResizingContainer : true,
34344     /**
34345      * @cfg {Number} columnWidth  width of the columns 
34346      */   
34347     
34348     columnWidth : 0,
34349     
34350     /**
34351      * @cfg {Number} maxCols maximum number of columns
34352      */   
34353     
34354     maxCols: 0,
34355     /**
34356      * @cfg {Number} padHeight padding below box..
34357      */   
34358     
34359     padHeight : 10, 
34360     
34361     /**
34362      * @cfg {Boolean} isAutoInitial defalut true
34363      */   
34364     
34365     isAutoInitial : true, 
34366     
34367     // private?
34368     gutter : 0,
34369     
34370     containerWidth: 0,
34371     initialColumnWidth : 0,
34372     currentSize : null,
34373     
34374     colYs : null, // array.
34375     maxY : 0,
34376     padWidth: 10,
34377     
34378     
34379     tag: 'div',
34380     cls: '',
34381     bricks: null, //CompositeElement
34382     cols : 0, // array?
34383     // element : null, // wrapped now this.el
34384     _isLayoutInited : null, 
34385     
34386     
34387     getAutoCreate : function(){
34388         
34389         var cfg = {
34390             tag: this.tag,
34391             cls: 'blog-masonary-wrapper ' + this.cls,
34392             cn : {
34393                 cls : 'mas-boxes masonary'
34394             }
34395         };
34396         
34397         return cfg;
34398     },
34399     
34400     getChildContainer: function( )
34401     {
34402         if (this.boxesEl) {
34403             return this.boxesEl;
34404         }
34405         
34406         this.boxesEl = this.el.select('.mas-boxes').first();
34407         
34408         return this.boxesEl;
34409     },
34410     
34411     
34412     initEvents : function()
34413     {
34414         var _this = this;
34415         
34416         if(this.isAutoInitial){
34417             Roo.log('hook children rendered');
34418             this.on('childrenrendered', function() {
34419                 Roo.log('children rendered');
34420                 _this.initial();
34421             } ,this);
34422         }
34423         
34424     },
34425     
34426     initial : function()
34427     {
34428         this.reloadItems();
34429
34430         this.currentSize = this.el.getBox(true);
34431
34432         /// was window resize... - let's see if this works..
34433         Roo.EventManager.onWindowResize(this.resize, this); 
34434
34435         if(!this.isAutoInitial){
34436             this.layout();
34437             return;
34438         }
34439         
34440         this.layout.defer(500,this);
34441     },
34442     
34443     reloadItems: function()
34444     {
34445         this.bricks = this.el.select('.masonry-brick', true);
34446         
34447         this.bricks.each(function(b) {
34448             //Roo.log(b.getSize());
34449             if (!b.attr('originalwidth')) {
34450                 b.attr('originalwidth',  b.getSize().width);
34451             }
34452             
34453         });
34454         
34455         Roo.log(this.bricks.elements.length);
34456     },
34457     
34458     resize : function()
34459     {
34460         Roo.log('resize');
34461         var cs = this.el.getBox(true);
34462         
34463         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34464             Roo.log("no change in with or X");
34465             return;
34466         }
34467         this.currentSize = cs;
34468         this.layout();
34469     },
34470     
34471     layout : function()
34472     {
34473          Roo.log('layout');
34474         this._resetLayout();
34475         //this._manageStamps();
34476       
34477         // don't animate first layout
34478         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34479         this.layoutItems( isInstant );
34480       
34481         // flag for initalized
34482         this._isLayoutInited = true;
34483     },
34484     
34485     layoutItems : function( isInstant )
34486     {
34487         //var items = this._getItemsForLayout( this.items );
34488         // original code supports filtering layout items.. we just ignore it..
34489         
34490         this._layoutItems( this.bricks , isInstant );
34491       
34492         this._postLayout();
34493     },
34494     _layoutItems : function ( items , isInstant)
34495     {
34496        //this.fireEvent( 'layout', this, items );
34497     
34498
34499         if ( !items || !items.elements.length ) {
34500           // no items, emit event with empty array
34501             return;
34502         }
34503
34504         var queue = [];
34505         items.each(function(item) {
34506             Roo.log("layout item");
34507             Roo.log(item);
34508             // get x/y object from method
34509             var position = this._getItemLayoutPosition( item );
34510             // enqueue
34511             position.item = item;
34512             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34513             queue.push( position );
34514         }, this);
34515       
34516         this._processLayoutQueue( queue );
34517     },
34518     /** Sets position of item in DOM
34519     * @param {Element} item
34520     * @param {Number} x - horizontal position
34521     * @param {Number} y - vertical position
34522     * @param {Boolean} isInstant - disables transitions
34523     */
34524     _processLayoutQueue : function( queue )
34525     {
34526         for ( var i=0, len = queue.length; i < len; i++ ) {
34527             var obj = queue[i];
34528             obj.item.position('absolute');
34529             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34530         }
34531     },
34532       
34533     
34534     /**
34535     * Any logic you want to do after each layout,
34536     * i.e. size the container
34537     */
34538     _postLayout : function()
34539     {
34540         this.resizeContainer();
34541     },
34542     
34543     resizeContainer : function()
34544     {
34545         if ( !this.isResizingContainer ) {
34546             return;
34547         }
34548         var size = this._getContainerSize();
34549         if ( size ) {
34550             this.el.setSize(size.width,size.height);
34551             this.boxesEl.setSize(size.width,size.height);
34552         }
34553     },
34554     
34555     
34556     
34557     _resetLayout : function()
34558     {
34559         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34560         this.colWidth = this.el.getWidth();
34561         //this.gutter = this.el.getWidth(); 
34562         
34563         this.measureColumns();
34564
34565         // reset column Y
34566         var i = this.cols;
34567         this.colYs = [];
34568         while (i--) {
34569             this.colYs.push( 0 );
34570         }
34571     
34572         this.maxY = 0;
34573     },
34574
34575     measureColumns : function()
34576     {
34577         this.getContainerWidth();
34578       // if columnWidth is 0, default to outerWidth of first item
34579         if ( !this.columnWidth ) {
34580             var firstItem = this.bricks.first();
34581             Roo.log(firstItem);
34582             this.columnWidth  = this.containerWidth;
34583             if (firstItem && firstItem.attr('originalwidth') ) {
34584                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34585             }
34586             // columnWidth fall back to item of first element
34587             Roo.log("set column width?");
34588                         this.initialColumnWidth = this.columnWidth  ;
34589
34590             // if first elem has no width, default to size of container
34591             
34592         }
34593         
34594         
34595         if (this.initialColumnWidth) {
34596             this.columnWidth = this.initialColumnWidth;
34597         }
34598         
34599         
34600             
34601         // column width is fixed at the top - however if container width get's smaller we should
34602         // reduce it...
34603         
34604         // this bit calcs how man columns..
34605             
34606         var columnWidth = this.columnWidth += this.gutter;
34607       
34608         // calculate columns
34609         var containerWidth = this.containerWidth + this.gutter;
34610         
34611         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34612         // fix rounding errors, typically with gutters
34613         var excess = columnWidth - containerWidth % columnWidth;
34614         
34615         
34616         // if overshoot is less than a pixel, round up, otherwise floor it
34617         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34618         cols = Math[ mathMethod ]( cols );
34619         this.cols = Math.max( cols, 1 );
34620         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34621         
34622          // padding positioning..
34623         var totalColWidth = this.cols * this.columnWidth;
34624         var padavail = this.containerWidth - totalColWidth;
34625         // so for 2 columns - we need 3 'pads'
34626         
34627         var padNeeded = (1+this.cols) * this.padWidth;
34628         
34629         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34630         
34631         this.columnWidth += padExtra
34632         //this.padWidth = Math.floor(padavail /  ( this.cols));
34633         
34634         // adjust colum width so that padding is fixed??
34635         
34636         // we have 3 columns ... total = width * 3
34637         // we have X left over... that should be used by 
34638         
34639         //if (this.expandC) {
34640             
34641         //}
34642         
34643         
34644         
34645     },
34646     
34647     getContainerWidth : function()
34648     {
34649        /* // container is parent if fit width
34650         var container = this.isFitWidth ? this.element.parentNode : this.element;
34651         // check that this.size and size are there
34652         // IE8 triggers resize on body size change, so they might not be
34653         
34654         var size = getSize( container );  //FIXME
34655         this.containerWidth = size && size.innerWidth; //FIXME
34656         */
34657          
34658         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34659         
34660     },
34661     
34662     _getItemLayoutPosition : function( item )  // what is item?
34663     {
34664         // we resize the item to our columnWidth..
34665       
34666         item.setWidth(this.columnWidth);
34667         item.autoBoxAdjust  = false;
34668         
34669         var sz = item.getSize();
34670  
34671         // how many columns does this brick span
34672         var remainder = this.containerWidth % this.columnWidth;
34673         
34674         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34675         // round if off by 1 pixel, otherwise use ceil
34676         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34677         colSpan = Math.min( colSpan, this.cols );
34678         
34679         // normally this should be '1' as we dont' currently allow multi width columns..
34680         
34681         var colGroup = this._getColGroup( colSpan );
34682         // get the minimum Y value from the columns
34683         var minimumY = Math.min.apply( Math, colGroup );
34684         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34685         
34686         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34687          
34688         // position the brick
34689         var position = {
34690             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34691             y: this.currentSize.y + minimumY + this.padHeight
34692         };
34693         
34694         Roo.log(position);
34695         // apply setHeight to necessary columns
34696         var setHeight = minimumY + sz.height + this.padHeight;
34697         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34698         
34699         var setSpan = this.cols + 1 - colGroup.length;
34700         for ( var i = 0; i < setSpan; i++ ) {
34701           this.colYs[ shortColIndex + i ] = setHeight ;
34702         }
34703       
34704         return position;
34705     },
34706     
34707     /**
34708      * @param {Number} colSpan - number of columns the element spans
34709      * @returns {Array} colGroup
34710      */
34711     _getColGroup : function( colSpan )
34712     {
34713         if ( colSpan < 2 ) {
34714           // if brick spans only one column, use all the column Ys
34715           return this.colYs;
34716         }
34717       
34718         var colGroup = [];
34719         // how many different places could this brick fit horizontally
34720         var groupCount = this.cols + 1 - colSpan;
34721         // for each group potential horizontal position
34722         for ( var i = 0; i < groupCount; i++ ) {
34723           // make an array of colY values for that one group
34724           var groupColYs = this.colYs.slice( i, i + colSpan );
34725           // and get the max value of the array
34726           colGroup[i] = Math.max.apply( Math, groupColYs );
34727         }
34728         return colGroup;
34729     },
34730     /*
34731     _manageStamp : function( stamp )
34732     {
34733         var stampSize =  stamp.getSize();
34734         var offset = stamp.getBox();
34735         // get the columns that this stamp affects
34736         var firstX = this.isOriginLeft ? offset.x : offset.right;
34737         var lastX = firstX + stampSize.width;
34738         var firstCol = Math.floor( firstX / this.columnWidth );
34739         firstCol = Math.max( 0, firstCol );
34740         
34741         var lastCol = Math.floor( lastX / this.columnWidth );
34742         // lastCol should not go over if multiple of columnWidth #425
34743         lastCol -= lastX % this.columnWidth ? 0 : 1;
34744         lastCol = Math.min( this.cols - 1, lastCol );
34745         
34746         // set colYs to bottom of the stamp
34747         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34748             stampSize.height;
34749             
34750         for ( var i = firstCol; i <= lastCol; i++ ) {
34751           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34752         }
34753     },
34754     */
34755     
34756     _getContainerSize : function()
34757     {
34758         this.maxY = Math.max.apply( Math, this.colYs );
34759         var size = {
34760             height: this.maxY
34761         };
34762       
34763         if ( this.isFitWidth ) {
34764             size.width = this._getContainerFitWidth();
34765         }
34766       
34767         return size;
34768     },
34769     
34770     _getContainerFitWidth : function()
34771     {
34772         var unusedCols = 0;
34773         // count unused columns
34774         var i = this.cols;
34775         while ( --i ) {
34776           if ( this.colYs[i] !== 0 ) {
34777             break;
34778           }
34779           unusedCols++;
34780         }
34781         // fit container to columns that have been used
34782         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34783     },
34784     
34785     needsResizeLayout : function()
34786     {
34787         var previousWidth = this.containerWidth;
34788         this.getContainerWidth();
34789         return previousWidth !== this.containerWidth;
34790     }
34791  
34792 });
34793
34794  
34795
34796  /*
34797  * - LGPL
34798  *
34799  * element
34800  * 
34801  */
34802
34803 /**
34804  * @class Roo.bootstrap.MasonryBrick
34805  * @extends Roo.bootstrap.Component
34806  * Bootstrap MasonryBrick class
34807  * 
34808  * @constructor
34809  * Create a new MasonryBrick
34810  * @param {Object} config The config object
34811  */
34812
34813 Roo.bootstrap.MasonryBrick = function(config){
34814     
34815     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34816     
34817     Roo.bootstrap.MasonryBrick.register(this);
34818     
34819     this.addEvents({
34820         // raw events
34821         /**
34822          * @event click
34823          * When a MasonryBrick is clcik
34824          * @param {Roo.bootstrap.MasonryBrick} this
34825          * @param {Roo.EventObject} e
34826          */
34827         "click" : true
34828     });
34829 };
34830
34831 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34832     
34833     /**
34834      * @cfg {String} title
34835      */   
34836     title : '',
34837     /**
34838      * @cfg {String} html
34839      */   
34840     html : '',
34841     /**
34842      * @cfg {String} bgimage
34843      */   
34844     bgimage : '',
34845     /**
34846      * @cfg {String} videourl
34847      */   
34848     videourl : '',
34849     /**
34850      * @cfg {String} cls
34851      */   
34852     cls : '',
34853     /**
34854      * @cfg {String} href
34855      */   
34856     href : '',
34857     /**
34858      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34859      */   
34860     size : 'xs',
34861     
34862     /**
34863      * @cfg {String} placetitle (center|bottom)
34864      */   
34865     placetitle : '',
34866     
34867     /**
34868      * @cfg {Boolean} isFitContainer defalut true
34869      */   
34870     isFitContainer : true, 
34871     
34872     /**
34873      * @cfg {Boolean} preventDefault defalut false
34874      */   
34875     preventDefault : false, 
34876     
34877     /**
34878      * @cfg {Boolean} inverse defalut false
34879      */   
34880     maskInverse : false, 
34881     
34882     getAutoCreate : function()
34883     {
34884         if(!this.isFitContainer){
34885             return this.getSplitAutoCreate();
34886         }
34887         
34888         var cls = 'masonry-brick masonry-brick-full';
34889         
34890         if(this.href.length){
34891             cls += ' masonry-brick-link';
34892         }
34893         
34894         if(this.bgimage.length){
34895             cls += ' masonry-brick-image';
34896         }
34897         
34898         if(this.maskInverse){
34899             cls += ' mask-inverse';
34900         }
34901         
34902         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34903             cls += ' enable-mask';
34904         }
34905         
34906         if(this.size){
34907             cls += ' masonry-' + this.size + '-brick';
34908         }
34909         
34910         if(this.placetitle.length){
34911             
34912             switch (this.placetitle) {
34913                 case 'center' :
34914                     cls += ' masonry-center-title';
34915                     break;
34916                 case 'bottom' :
34917                     cls += ' masonry-bottom-title';
34918                     break;
34919                 default:
34920                     break;
34921             }
34922             
34923         } else {
34924             if(!this.html.length && !this.bgimage.length){
34925                 cls += ' masonry-center-title';
34926             }
34927
34928             if(!this.html.length && this.bgimage.length){
34929                 cls += ' masonry-bottom-title';
34930             }
34931         }
34932         
34933         if(this.cls){
34934             cls += ' ' + this.cls;
34935         }
34936         
34937         var cfg = {
34938             tag: (this.href.length) ? 'a' : 'div',
34939             cls: cls,
34940             cn: [
34941                 {
34942                     tag: 'div',
34943                     cls: 'masonry-brick-mask'
34944                 },
34945                 {
34946                     tag: 'div',
34947                     cls: 'masonry-brick-paragraph',
34948                     cn: []
34949                 }
34950             ]
34951         };
34952         
34953         if(this.href.length){
34954             cfg.href = this.href;
34955         }
34956         
34957         var cn = cfg.cn[1].cn;
34958         
34959         if(this.title.length){
34960             cn.push({
34961                 tag: 'h4',
34962                 cls: 'masonry-brick-title',
34963                 html: this.title
34964             });
34965         }
34966         
34967         if(this.html.length){
34968             cn.push({
34969                 tag: 'p',
34970                 cls: 'masonry-brick-text',
34971                 html: this.html
34972             });
34973         }
34974         
34975         if (!this.title.length && !this.html.length) {
34976             cfg.cn[1].cls += ' hide';
34977         }
34978         
34979         if(this.bgimage.length){
34980             cfg.cn.push({
34981                 tag: 'img',
34982                 cls: 'masonry-brick-image-view',
34983                 src: this.bgimage
34984             });
34985         }
34986         
34987         if(this.videourl.length){
34988             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34989             // youtube support only?
34990             cfg.cn.push({
34991                 tag: 'iframe',
34992                 cls: 'masonry-brick-image-view',
34993                 src: vurl,
34994                 frameborder : 0,
34995                 allowfullscreen : true
34996             });
34997         }
34998         
34999         return cfg;
35000         
35001     },
35002     
35003     getSplitAutoCreate : function()
35004     {
35005         var cls = 'masonry-brick masonry-brick-split';
35006         
35007         if(this.href.length){
35008             cls += ' masonry-brick-link';
35009         }
35010         
35011         if(this.bgimage.length){
35012             cls += ' masonry-brick-image';
35013         }
35014         
35015         if(this.size){
35016             cls += ' masonry-' + this.size + '-brick';
35017         }
35018         
35019         switch (this.placetitle) {
35020             case 'center' :
35021                 cls += ' masonry-center-title';
35022                 break;
35023             case 'bottom' :
35024                 cls += ' masonry-bottom-title';
35025                 break;
35026             default:
35027                 if(!this.bgimage.length){
35028                     cls += ' masonry-center-title';
35029                 }
35030
35031                 if(this.bgimage.length){
35032                     cls += ' masonry-bottom-title';
35033                 }
35034                 break;
35035         }
35036         
35037         if(this.cls){
35038             cls += ' ' + this.cls;
35039         }
35040         
35041         var cfg = {
35042             tag: (this.href.length) ? 'a' : 'div',
35043             cls: cls,
35044             cn: [
35045                 {
35046                     tag: 'div',
35047                     cls: 'masonry-brick-split-head',
35048                     cn: [
35049                         {
35050                             tag: 'div',
35051                             cls: 'masonry-brick-paragraph',
35052                             cn: []
35053                         }
35054                     ]
35055                 },
35056                 {
35057                     tag: 'div',
35058                     cls: 'masonry-brick-split-body',
35059                     cn: []
35060                 }
35061             ]
35062         };
35063         
35064         if(this.href.length){
35065             cfg.href = this.href;
35066         }
35067         
35068         if(this.title.length){
35069             cfg.cn[0].cn[0].cn.push({
35070                 tag: 'h4',
35071                 cls: 'masonry-brick-title',
35072                 html: this.title
35073             });
35074         }
35075         
35076         if(this.html.length){
35077             cfg.cn[1].cn.push({
35078                 tag: 'p',
35079                 cls: 'masonry-brick-text',
35080                 html: this.html
35081             });
35082         }
35083
35084         if(this.bgimage.length){
35085             cfg.cn[0].cn.push({
35086                 tag: 'img',
35087                 cls: 'masonry-brick-image-view',
35088                 src: this.bgimage
35089             });
35090         }
35091         
35092         if(this.videourl.length){
35093             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35094             // youtube support only?
35095             cfg.cn[0].cn.cn.push({
35096                 tag: 'iframe',
35097                 cls: 'masonry-brick-image-view',
35098                 src: vurl,
35099                 frameborder : 0,
35100                 allowfullscreen : true
35101             });
35102         }
35103         
35104         return cfg;
35105     },
35106     
35107     initEvents: function() 
35108     {
35109         switch (this.size) {
35110             case 'xs' :
35111                 this.x = 1;
35112                 this.y = 1;
35113                 break;
35114             case 'sm' :
35115                 this.x = 2;
35116                 this.y = 2;
35117                 break;
35118             case 'md' :
35119             case 'md-left' :
35120             case 'md-right' :
35121                 this.x = 3;
35122                 this.y = 3;
35123                 break;
35124             case 'tall' :
35125                 this.x = 2;
35126                 this.y = 3;
35127                 break;
35128             case 'wide' :
35129                 this.x = 3;
35130                 this.y = 2;
35131                 break;
35132             case 'wide-thin' :
35133                 this.x = 3;
35134                 this.y = 1;
35135                 break;
35136                         
35137             default :
35138                 break;
35139         }
35140         
35141         if(Roo.isTouch){
35142             this.el.on('touchstart', this.onTouchStart, this);
35143             this.el.on('touchmove', this.onTouchMove, this);
35144             this.el.on('touchend', this.onTouchEnd, this);
35145             this.el.on('contextmenu', this.onContextMenu, this);
35146         } else {
35147             this.el.on('mouseenter'  ,this.enter, this);
35148             this.el.on('mouseleave', this.leave, this);
35149             this.el.on('click', this.onClick, this);
35150         }
35151         
35152         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35153             this.parent().bricks.push(this);   
35154         }
35155         
35156     },
35157     
35158     onClick: function(e, el)
35159     {
35160         var time = this.endTimer - this.startTimer;
35161         // Roo.log(e.preventDefault());
35162         if(Roo.isTouch){
35163             if(time > 1000){
35164                 e.preventDefault();
35165                 return;
35166             }
35167         }
35168         
35169         if(!this.preventDefault){
35170             return;
35171         }
35172         
35173         e.preventDefault();
35174         
35175         if (this.activeClass != '') {
35176             this.selectBrick();
35177         }
35178         
35179         this.fireEvent('click', this, e);
35180     },
35181     
35182     enter: function(e, el)
35183     {
35184         e.preventDefault();
35185         
35186         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35187             return;
35188         }
35189         
35190         if(this.bgimage.length && this.html.length){
35191             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35192         }
35193     },
35194     
35195     leave: function(e, el)
35196     {
35197         e.preventDefault();
35198         
35199         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35200             return;
35201         }
35202         
35203         if(this.bgimage.length && this.html.length){
35204             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35205         }
35206     },
35207     
35208     onTouchStart: function(e, el)
35209     {
35210 //        e.preventDefault();
35211         
35212         this.touchmoved = false;
35213         
35214         if(!this.isFitContainer){
35215             return;
35216         }
35217         
35218         if(!this.bgimage.length || !this.html.length){
35219             return;
35220         }
35221         
35222         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35223         
35224         this.timer = new Date().getTime();
35225         
35226     },
35227     
35228     onTouchMove: function(e, el)
35229     {
35230         this.touchmoved = true;
35231     },
35232     
35233     onContextMenu : function(e,el)
35234     {
35235         e.preventDefault();
35236         e.stopPropagation();
35237         return false;
35238     },
35239     
35240     onTouchEnd: function(e, el)
35241     {
35242 //        e.preventDefault();
35243         
35244         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35245         
35246             this.leave(e,el);
35247             
35248             return;
35249         }
35250         
35251         if(!this.bgimage.length || !this.html.length){
35252             
35253             if(this.href.length){
35254                 window.location.href = this.href;
35255             }
35256             
35257             return;
35258         }
35259         
35260         if(!this.isFitContainer){
35261             return;
35262         }
35263         
35264         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35265         
35266         window.location.href = this.href;
35267     },
35268     
35269     //selection on single brick only
35270     selectBrick : function() {
35271         
35272         if (!this.parentId) {
35273             return;
35274         }
35275         
35276         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35277         var index = m.selectedBrick.indexOf(this.id);
35278         
35279         if ( index > -1) {
35280             m.selectedBrick.splice(index,1);
35281             this.el.removeClass(this.activeClass);
35282             return;
35283         }
35284         
35285         for(var i = 0; i < m.selectedBrick.length; i++) {
35286             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35287             b.el.removeClass(b.activeClass);
35288         }
35289         
35290         m.selectedBrick = [];
35291         
35292         m.selectedBrick.push(this.id);
35293         this.el.addClass(this.activeClass);
35294         return;
35295     },
35296     
35297     isSelected : function(){
35298         return this.el.hasClass(this.activeClass);
35299         
35300     }
35301 });
35302
35303 Roo.apply(Roo.bootstrap.MasonryBrick, {
35304     
35305     //groups: {},
35306     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35307      /**
35308     * register a Masonry Brick
35309     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35310     */
35311     
35312     register : function(brick)
35313     {
35314         //this.groups[brick.id] = brick;
35315         this.groups.add(brick.id, brick);
35316     },
35317     /**
35318     * fetch a  masonry brick based on the masonry brick ID
35319     * @param {string} the masonry brick to add
35320     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35321     */
35322     
35323     get: function(brick_id) 
35324     {
35325         // if (typeof(this.groups[brick_id]) == 'undefined') {
35326         //     return false;
35327         // }
35328         // return this.groups[brick_id] ;
35329         
35330         if(this.groups.key(brick_id)) {
35331             return this.groups.key(brick_id);
35332         }
35333         
35334         return false;
35335     }
35336     
35337     
35338     
35339 });
35340
35341  /*
35342  * - LGPL
35343  *
35344  * element
35345  * 
35346  */
35347
35348 /**
35349  * @class Roo.bootstrap.Brick
35350  * @extends Roo.bootstrap.Component
35351  * Bootstrap Brick class
35352  * 
35353  * @constructor
35354  * Create a new Brick
35355  * @param {Object} config The config object
35356  */
35357
35358 Roo.bootstrap.Brick = function(config){
35359     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35360     
35361     this.addEvents({
35362         // raw events
35363         /**
35364          * @event click
35365          * When a Brick is click
35366          * @param {Roo.bootstrap.Brick} this
35367          * @param {Roo.EventObject} e
35368          */
35369         "click" : true
35370     });
35371 };
35372
35373 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35374     
35375     /**
35376      * @cfg {String} title
35377      */   
35378     title : '',
35379     /**
35380      * @cfg {String} html
35381      */   
35382     html : '',
35383     /**
35384      * @cfg {String} bgimage
35385      */   
35386     bgimage : '',
35387     /**
35388      * @cfg {String} cls
35389      */   
35390     cls : '',
35391     /**
35392      * @cfg {String} href
35393      */   
35394     href : '',
35395     /**
35396      * @cfg {String} video
35397      */   
35398     video : '',
35399     /**
35400      * @cfg {Boolean} square
35401      */   
35402     square : true,
35403     
35404     getAutoCreate : function()
35405     {
35406         var cls = 'roo-brick';
35407         
35408         if(this.href.length){
35409             cls += ' roo-brick-link';
35410         }
35411         
35412         if(this.bgimage.length){
35413             cls += ' roo-brick-image';
35414         }
35415         
35416         if(!this.html.length && !this.bgimage.length){
35417             cls += ' roo-brick-center-title';
35418         }
35419         
35420         if(!this.html.length && this.bgimage.length){
35421             cls += ' roo-brick-bottom-title';
35422         }
35423         
35424         if(this.cls){
35425             cls += ' ' + this.cls;
35426         }
35427         
35428         var cfg = {
35429             tag: (this.href.length) ? 'a' : 'div',
35430             cls: cls,
35431             cn: [
35432                 {
35433                     tag: 'div',
35434                     cls: 'roo-brick-paragraph',
35435                     cn: []
35436                 }
35437             ]
35438         };
35439         
35440         if(this.href.length){
35441             cfg.href = this.href;
35442         }
35443         
35444         var cn = cfg.cn[0].cn;
35445         
35446         if(this.title.length){
35447             cn.push({
35448                 tag: 'h4',
35449                 cls: 'roo-brick-title',
35450                 html: this.title
35451             });
35452         }
35453         
35454         if(this.html.length){
35455             cn.push({
35456                 tag: 'p',
35457                 cls: 'roo-brick-text',
35458                 html: this.html
35459             });
35460         } else {
35461             cn.cls += ' hide';
35462         }
35463         
35464         if(this.bgimage.length){
35465             cfg.cn.push({
35466                 tag: 'img',
35467                 cls: 'roo-brick-image-view',
35468                 src: this.bgimage
35469             });
35470         }
35471         
35472         return cfg;
35473     },
35474     
35475     initEvents: function() 
35476     {
35477         if(this.title.length || this.html.length){
35478             this.el.on('mouseenter'  ,this.enter, this);
35479             this.el.on('mouseleave', this.leave, this);
35480         }
35481         
35482         Roo.EventManager.onWindowResize(this.resize, this); 
35483         
35484         if(this.bgimage.length){
35485             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35486             this.imageEl.on('load', this.onImageLoad, this);
35487             return;
35488         }
35489         
35490         this.resize();
35491     },
35492     
35493     onImageLoad : function()
35494     {
35495         this.resize();
35496     },
35497     
35498     resize : function()
35499     {
35500         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35501         
35502         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35503         
35504         if(this.bgimage.length){
35505             var image = this.el.select('.roo-brick-image-view', true).first();
35506             
35507             image.setWidth(paragraph.getWidth());
35508             
35509             if(this.square){
35510                 image.setHeight(paragraph.getWidth());
35511             }
35512             
35513             this.el.setHeight(image.getHeight());
35514             paragraph.setHeight(image.getHeight());
35515             
35516         }
35517         
35518     },
35519     
35520     enter: function(e, el)
35521     {
35522         e.preventDefault();
35523         
35524         if(this.bgimage.length){
35525             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35526             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35527         }
35528     },
35529     
35530     leave: function(e, el)
35531     {
35532         e.preventDefault();
35533         
35534         if(this.bgimage.length){
35535             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35536             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35537         }
35538     }
35539     
35540 });
35541
35542  
35543
35544  /*
35545  * - LGPL
35546  *
35547  * Number field 
35548  */
35549
35550 /**
35551  * @class Roo.bootstrap.NumberField
35552  * @extends Roo.bootstrap.Input
35553  * Bootstrap NumberField class
35554  * 
35555  * 
35556  * 
35557  * 
35558  * @constructor
35559  * Create a new NumberField
35560  * @param {Object} config The config object
35561  */
35562
35563 Roo.bootstrap.NumberField = function(config){
35564     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35565 };
35566
35567 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35568     
35569     /**
35570      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35571      */
35572     allowDecimals : true,
35573     /**
35574      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35575      */
35576     decimalSeparator : ".",
35577     /**
35578      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35579      */
35580     decimalPrecision : 2,
35581     /**
35582      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35583      */
35584     allowNegative : true,
35585     
35586     /**
35587      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35588      */
35589     allowZero: true,
35590     /**
35591      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35592      */
35593     minValue : Number.NEGATIVE_INFINITY,
35594     /**
35595      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35596      */
35597     maxValue : Number.MAX_VALUE,
35598     /**
35599      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35600      */
35601     minText : "The minimum value for this field is {0}",
35602     /**
35603      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35604      */
35605     maxText : "The maximum value for this field is {0}",
35606     /**
35607      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35608      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35609      */
35610     nanText : "{0} is not a valid number",
35611     /**
35612      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35613      */
35614     thousandsDelimiter : false,
35615     /**
35616      * @cfg {String} valueAlign alignment of value
35617      */
35618     valueAlign : "left",
35619
35620     getAutoCreate : function()
35621     {
35622         var hiddenInput = {
35623             tag: 'input',
35624             type: 'hidden',
35625             id: Roo.id(),
35626             cls: 'hidden-number-input'
35627         };
35628         
35629         if (this.name) {
35630             hiddenInput.name = this.name;
35631         }
35632         
35633         this.name = '';
35634         
35635         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35636         
35637         this.name = hiddenInput.name;
35638         
35639         if(cfg.cn.length > 0) {
35640             cfg.cn.push(hiddenInput);
35641         }
35642         
35643         return cfg;
35644     },
35645
35646     // private
35647     initEvents : function()
35648     {   
35649         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35650         
35651         var allowed = "0123456789";
35652         
35653         if(this.allowDecimals){
35654             allowed += this.decimalSeparator;
35655         }
35656         
35657         if(this.allowNegative){
35658             allowed += "-";
35659         }
35660         
35661         if(this.thousandsDelimiter) {
35662             allowed += ",";
35663         }
35664         
35665         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35666         
35667         var keyPress = function(e){
35668             
35669             var k = e.getKey();
35670             
35671             var c = e.getCharCode();
35672             
35673             if(
35674                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35675                     allowed.indexOf(String.fromCharCode(c)) === -1
35676             ){
35677                 e.stopEvent();
35678                 return;
35679             }
35680             
35681             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35682                 return;
35683             }
35684             
35685             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35686                 e.stopEvent();
35687             }
35688         };
35689         
35690         this.el.on("keypress", keyPress, this);
35691     },
35692     
35693     validateValue : function(value)
35694     {
35695         
35696         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35697             return false;
35698         }
35699         
35700         var num = this.parseValue(value);
35701         
35702         if(isNaN(num)){
35703             this.markInvalid(String.format(this.nanText, value));
35704             return false;
35705         }
35706         
35707         if(num < this.minValue){
35708             this.markInvalid(String.format(this.minText, this.minValue));
35709             return false;
35710         }
35711         
35712         if(num > this.maxValue){
35713             this.markInvalid(String.format(this.maxText, this.maxValue));
35714             return false;
35715         }
35716         
35717         return true;
35718     },
35719
35720     getValue : function()
35721     {
35722         var v = this.hiddenEl().getValue();
35723         
35724         return this.fixPrecision(this.parseValue(v));
35725     },
35726
35727     parseValue : function(value)
35728     {
35729         if(this.thousandsDelimiter) {
35730             value += "";
35731             r = new RegExp(",", "g");
35732             value = value.replace(r, "");
35733         }
35734         
35735         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35736         return isNaN(value) ? '' : value;
35737     },
35738
35739     fixPrecision : function(value)
35740     {
35741         if(this.thousandsDelimiter) {
35742             value += "";
35743             r = new RegExp(",", "g");
35744             value = value.replace(r, "");
35745         }
35746         
35747         var nan = isNaN(value);
35748         
35749         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35750             return nan ? '' : value;
35751         }
35752         return parseFloat(value).toFixed(this.decimalPrecision);
35753     },
35754
35755     setValue : function(v)
35756     {
35757         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35758         
35759         this.value = v;
35760         
35761         if(this.rendered){
35762             
35763             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35764             
35765             this.inputEl().dom.value = (v == '') ? '' :
35766                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35767             
35768             if(!this.allowZero && v === '0') {
35769                 this.hiddenEl().dom.value = '';
35770                 this.inputEl().dom.value = '';
35771             }
35772             
35773             this.validate();
35774         }
35775     },
35776
35777     decimalPrecisionFcn : function(v)
35778     {
35779         return Math.floor(v);
35780     },
35781
35782     beforeBlur : function()
35783     {
35784         var v = this.parseValue(this.getRawValue());
35785         
35786         if(v || v === 0 || v === ''){
35787             this.setValue(v);
35788         }
35789     },
35790     
35791     hiddenEl : function()
35792     {
35793         return this.el.select('input.hidden-number-input',true).first();
35794     }
35795     
35796 });
35797
35798  
35799
35800 /*
35801 * Licence: LGPL
35802 */
35803
35804 /**
35805  * @class Roo.bootstrap.DocumentSlider
35806  * @extends Roo.bootstrap.Component
35807  * Bootstrap DocumentSlider class
35808  * 
35809  * @constructor
35810  * Create a new DocumentViewer
35811  * @param {Object} config The config object
35812  */
35813
35814 Roo.bootstrap.DocumentSlider = function(config){
35815     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35816     
35817     this.files = [];
35818     
35819     this.addEvents({
35820         /**
35821          * @event initial
35822          * Fire after initEvent
35823          * @param {Roo.bootstrap.DocumentSlider} this
35824          */
35825         "initial" : true,
35826         /**
35827          * @event update
35828          * Fire after update
35829          * @param {Roo.bootstrap.DocumentSlider} this
35830          */
35831         "update" : true,
35832         /**
35833          * @event click
35834          * Fire after click
35835          * @param {Roo.bootstrap.DocumentSlider} this
35836          */
35837         "click" : true
35838     });
35839 };
35840
35841 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35842     
35843     files : false,
35844     
35845     indicator : 0,
35846     
35847     getAutoCreate : function()
35848     {
35849         var cfg = {
35850             tag : 'div',
35851             cls : 'roo-document-slider',
35852             cn : [
35853                 {
35854                     tag : 'div',
35855                     cls : 'roo-document-slider-header',
35856                     cn : [
35857                         {
35858                             tag : 'div',
35859                             cls : 'roo-document-slider-header-title'
35860                         }
35861                     ]
35862                 },
35863                 {
35864                     tag : 'div',
35865                     cls : 'roo-document-slider-body',
35866                     cn : [
35867                         {
35868                             tag : 'div',
35869                             cls : 'roo-document-slider-prev',
35870                             cn : [
35871                                 {
35872                                     tag : 'i',
35873                                     cls : 'fa fa-chevron-left'
35874                                 }
35875                             ]
35876                         },
35877                         {
35878                             tag : 'div',
35879                             cls : 'roo-document-slider-thumb',
35880                             cn : [
35881                                 {
35882                                     tag : 'img',
35883                                     cls : 'roo-document-slider-image'
35884                                 }
35885                             ]
35886                         },
35887                         {
35888                             tag : 'div',
35889                             cls : 'roo-document-slider-next',
35890                             cn : [
35891                                 {
35892                                     tag : 'i',
35893                                     cls : 'fa fa-chevron-right'
35894                                 }
35895                             ]
35896                         }
35897                     ]
35898                 }
35899             ]
35900         };
35901         
35902         return cfg;
35903     },
35904     
35905     initEvents : function()
35906     {
35907         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35908         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35909         
35910         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35911         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35912         
35913         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35914         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35915         
35916         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35917         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35918         
35919         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35920         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35921         
35922         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35923         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35924         
35925         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35926         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35927         
35928         this.thumbEl.on('click', this.onClick, this);
35929         
35930         this.prevIndicator.on('click', this.prev, this);
35931         
35932         this.nextIndicator.on('click', this.next, this);
35933         
35934     },
35935     
35936     initial : function()
35937     {
35938         if(this.files.length){
35939             this.indicator = 1;
35940             this.update()
35941         }
35942         
35943         this.fireEvent('initial', this);
35944     },
35945     
35946     update : function()
35947     {
35948         this.imageEl.attr('src', this.files[this.indicator - 1]);
35949         
35950         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35951         
35952         this.prevIndicator.show();
35953         
35954         if(this.indicator == 1){
35955             this.prevIndicator.hide();
35956         }
35957         
35958         this.nextIndicator.show();
35959         
35960         if(this.indicator == this.files.length){
35961             this.nextIndicator.hide();
35962         }
35963         
35964         this.thumbEl.scrollTo('top');
35965         
35966         this.fireEvent('update', this);
35967     },
35968     
35969     onClick : function(e)
35970     {
35971         e.preventDefault();
35972         
35973         this.fireEvent('click', this);
35974     },
35975     
35976     prev : function(e)
35977     {
35978         e.preventDefault();
35979         
35980         this.indicator = Math.max(1, this.indicator - 1);
35981         
35982         this.update();
35983     },
35984     
35985     next : function(e)
35986     {
35987         e.preventDefault();
35988         
35989         this.indicator = Math.min(this.files.length, this.indicator + 1);
35990         
35991         this.update();
35992     }
35993 });
35994 /*
35995  * - LGPL
35996  *
35997  * RadioSet
35998  *
35999  *
36000  */
36001
36002 /**
36003  * @class Roo.bootstrap.RadioSet
36004  * @extends Roo.bootstrap.Input
36005  * Bootstrap RadioSet class
36006  * @cfg {String} indicatorpos (left|right) default left
36007  * @cfg {Boolean} inline (true|false) inline the element (default true)
36008  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36009  * @constructor
36010  * Create a new RadioSet
36011  * @param {Object} config The config object
36012  */
36013
36014 Roo.bootstrap.RadioSet = function(config){
36015     
36016     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36017     
36018     this.radioes = [];
36019     
36020     Roo.bootstrap.RadioSet.register(this);
36021     
36022     this.addEvents({
36023         /**
36024         * @event check
36025         * Fires when the element is checked or unchecked.
36026         * @param {Roo.bootstrap.RadioSet} this This radio
36027         * @param {Roo.bootstrap.Radio} item The checked item
36028         */
36029        check : true,
36030        /**
36031         * @event click
36032         * Fires when the element is click.
36033         * @param {Roo.bootstrap.RadioSet} this This radio set
36034         * @param {Roo.bootstrap.Radio} item The checked item
36035         * @param {Roo.EventObject} e The event object
36036         */
36037        click : true
36038     });
36039     
36040 };
36041
36042 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36043
36044     radioes : false,
36045     
36046     inline : true,
36047     
36048     weight : '',
36049     
36050     indicatorpos : 'left',
36051     
36052     getAutoCreate : function()
36053     {
36054         var label = {
36055             tag : 'label',
36056             cls : 'roo-radio-set-label',
36057             cn : [
36058                 {
36059                     tag : 'span',
36060                     html : this.fieldLabel
36061                 }
36062             ]
36063         };
36064         if (Roo.bootstrap.version == 3) {
36065             
36066             
36067             if(this.indicatorpos == 'left'){
36068                 label.cn.unshift({
36069                     tag : 'i',
36070                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36071                     tooltip : 'This field is required'
36072                 });
36073             } else {
36074                 label.cn.push({
36075                     tag : 'i',
36076                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36077                     tooltip : 'This field is required'
36078                 });
36079             }
36080         }
36081         var items = {
36082             tag : 'div',
36083             cls : 'roo-radio-set-items'
36084         };
36085         
36086         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36087         
36088         if (align === 'left' && this.fieldLabel.length) {
36089             
36090             items = {
36091                 cls : "roo-radio-set-right", 
36092                 cn: [
36093                     items
36094                 ]
36095             };
36096             
36097             if(this.labelWidth > 12){
36098                 label.style = "width: " + this.labelWidth + 'px';
36099             }
36100             
36101             if(this.labelWidth < 13 && this.labelmd == 0){
36102                 this.labelmd = this.labelWidth;
36103             }
36104             
36105             if(this.labellg > 0){
36106                 label.cls += ' col-lg-' + this.labellg;
36107                 items.cls += ' col-lg-' + (12 - this.labellg);
36108             }
36109             
36110             if(this.labelmd > 0){
36111                 label.cls += ' col-md-' + this.labelmd;
36112                 items.cls += ' col-md-' + (12 - this.labelmd);
36113             }
36114             
36115             if(this.labelsm > 0){
36116                 label.cls += ' col-sm-' + this.labelsm;
36117                 items.cls += ' col-sm-' + (12 - this.labelsm);
36118             }
36119             
36120             if(this.labelxs > 0){
36121                 label.cls += ' col-xs-' + this.labelxs;
36122                 items.cls += ' col-xs-' + (12 - this.labelxs);
36123             }
36124         }
36125         
36126         var cfg = {
36127             tag : 'div',
36128             cls : 'roo-radio-set',
36129             cn : [
36130                 {
36131                     tag : 'input',
36132                     cls : 'roo-radio-set-input',
36133                     type : 'hidden',
36134                     name : this.name,
36135                     value : this.value ? this.value :  ''
36136                 },
36137                 label,
36138                 items
36139             ]
36140         };
36141         
36142         if(this.weight.length){
36143             cfg.cls += ' roo-radio-' + this.weight;
36144         }
36145         
36146         if(this.inline) {
36147             cfg.cls += ' roo-radio-set-inline';
36148         }
36149         
36150         var settings=this;
36151         ['xs','sm','md','lg'].map(function(size){
36152             if (settings[size]) {
36153                 cfg.cls += ' col-' + size + '-' + settings[size];
36154             }
36155         });
36156         
36157         return cfg;
36158         
36159     },
36160
36161     initEvents : function()
36162     {
36163         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36164         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36165         
36166         if(!this.fieldLabel.length){
36167             this.labelEl.hide();
36168         }
36169         
36170         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36171         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36172         
36173         this.indicator = this.indicatorEl();
36174         
36175         if(this.indicator){
36176             this.indicator.addClass('invisible');
36177         }
36178         
36179         this.originalValue = this.getValue();
36180         
36181     },
36182     
36183     inputEl: function ()
36184     {
36185         return this.el.select('.roo-radio-set-input', true).first();
36186     },
36187     
36188     getChildContainer : function()
36189     {
36190         return this.itemsEl;
36191     },
36192     
36193     register : function(item)
36194     {
36195         this.radioes.push(item);
36196         
36197     },
36198     
36199     validate : function()
36200     {   
36201         if(this.getVisibilityEl().hasClass('hidden')){
36202             return true;
36203         }
36204         
36205         var valid = false;
36206         
36207         Roo.each(this.radioes, function(i){
36208             if(!i.checked){
36209                 return;
36210             }
36211             
36212             valid = true;
36213             return false;
36214         });
36215         
36216         if(this.allowBlank) {
36217             return true;
36218         }
36219         
36220         if(this.disabled || valid){
36221             this.markValid();
36222             return true;
36223         }
36224         
36225         this.markInvalid();
36226         return false;
36227         
36228     },
36229     
36230     markValid : function()
36231     {
36232         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36233             this.indicatorEl().removeClass('visible');
36234             this.indicatorEl().addClass('invisible');
36235         }
36236         
36237         
36238         if (Roo.bootstrap.version == 3) {
36239             this.el.removeClass([this.invalidClass, this.validClass]);
36240             this.el.addClass(this.validClass);
36241         } else {
36242             this.el.removeClass(['is-invalid','is-valid']);
36243             this.el.addClass(['is-valid']);
36244         }
36245         this.fireEvent('valid', this);
36246     },
36247     
36248     markInvalid : function(msg)
36249     {
36250         if(this.allowBlank || this.disabled){
36251             return;
36252         }
36253         
36254         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36255             this.indicatorEl().removeClass('invisible');
36256             this.indicatorEl().addClass('visible');
36257         }
36258         if (Roo.bootstrap.version == 3) {
36259             this.el.removeClass([this.invalidClass, this.validClass]);
36260             this.el.addClass(this.invalidClass);
36261         } else {
36262             this.el.removeClass(['is-invalid','is-valid']);
36263             this.el.addClass(['is-invalid']);
36264         }
36265         
36266         this.fireEvent('invalid', this, msg);
36267         
36268     },
36269     
36270     setValue : function(v, suppressEvent)
36271     {   
36272         if(this.value === v){
36273             return;
36274         }
36275         
36276         this.value = v;
36277         
36278         if(this.rendered){
36279             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36280         }
36281         
36282         Roo.each(this.radioes, function(i){
36283             i.checked = false;
36284             i.el.removeClass('checked');
36285         });
36286         
36287         Roo.each(this.radioes, function(i){
36288             
36289             if(i.value === v || i.value.toString() === v.toString()){
36290                 i.checked = true;
36291                 i.el.addClass('checked');
36292                 
36293                 if(suppressEvent !== true){
36294                     this.fireEvent('check', this, i);
36295                 }
36296                 
36297                 return false;
36298             }
36299             
36300         }, this);
36301         
36302         this.validate();
36303     },
36304     
36305     clearInvalid : function(){
36306         
36307         if(!this.el || this.preventMark){
36308             return;
36309         }
36310         
36311         this.el.removeClass([this.invalidClass]);
36312         
36313         this.fireEvent('valid', this);
36314     }
36315     
36316 });
36317
36318 Roo.apply(Roo.bootstrap.RadioSet, {
36319     
36320     groups: {},
36321     
36322     register : function(set)
36323     {
36324         this.groups[set.name] = set;
36325     },
36326     
36327     get: function(name) 
36328     {
36329         if (typeof(this.groups[name]) == 'undefined') {
36330             return false;
36331         }
36332         
36333         return this.groups[name] ;
36334     }
36335     
36336 });
36337 /*
36338  * Based on:
36339  * Ext JS Library 1.1.1
36340  * Copyright(c) 2006-2007, Ext JS, LLC.
36341  *
36342  * Originally Released Under LGPL - original licence link has changed is not relivant.
36343  *
36344  * Fork - LGPL
36345  * <script type="text/javascript">
36346  */
36347
36348
36349 /**
36350  * @class Roo.bootstrap.SplitBar
36351  * @extends Roo.util.Observable
36352  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36353  * <br><br>
36354  * Usage:
36355  * <pre><code>
36356 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36357                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36358 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36359 split.minSize = 100;
36360 split.maxSize = 600;
36361 split.animate = true;
36362 split.on('moved', splitterMoved);
36363 </code></pre>
36364  * @constructor
36365  * Create a new SplitBar
36366  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36367  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36368  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36369  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36370                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36371                         position of the SplitBar).
36372  */
36373 Roo.bootstrap.SplitBar = function(cfg){
36374     
36375     /** @private */
36376     
36377     //{
36378     //  dragElement : elm
36379     //  resizingElement: el,
36380         // optional..
36381     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36382     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36383         // existingProxy ???
36384     //}
36385     
36386     this.el = Roo.get(cfg.dragElement, true);
36387     this.el.dom.unselectable = "on";
36388     /** @private */
36389     this.resizingEl = Roo.get(cfg.resizingElement, true);
36390
36391     /**
36392      * @private
36393      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36394      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36395      * @type Number
36396      */
36397     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36398     
36399     /**
36400      * The minimum size of the resizing element. (Defaults to 0)
36401      * @type Number
36402      */
36403     this.minSize = 0;
36404     
36405     /**
36406      * The maximum size of the resizing element. (Defaults to 2000)
36407      * @type Number
36408      */
36409     this.maxSize = 2000;
36410     
36411     /**
36412      * Whether to animate the transition to the new size
36413      * @type Boolean
36414      */
36415     this.animate = false;
36416     
36417     /**
36418      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36419      * @type Boolean
36420      */
36421     this.useShim = false;
36422     
36423     /** @private */
36424     this.shim = null;
36425     
36426     if(!cfg.existingProxy){
36427         /** @private */
36428         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36429     }else{
36430         this.proxy = Roo.get(cfg.existingProxy).dom;
36431     }
36432     /** @private */
36433     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36434     
36435     /** @private */
36436     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36437     
36438     /** @private */
36439     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36440     
36441     /** @private */
36442     this.dragSpecs = {};
36443     
36444     /**
36445      * @private The adapter to use to positon and resize elements
36446      */
36447     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36448     this.adapter.init(this);
36449     
36450     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36451         /** @private */
36452         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36453         this.el.addClass("roo-splitbar-h");
36454     }else{
36455         /** @private */
36456         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36457         this.el.addClass("roo-splitbar-v");
36458     }
36459     
36460     this.addEvents({
36461         /**
36462          * @event resize
36463          * Fires when the splitter is moved (alias for {@link #event-moved})
36464          * @param {Roo.bootstrap.SplitBar} this
36465          * @param {Number} newSize the new width or height
36466          */
36467         "resize" : true,
36468         /**
36469          * @event moved
36470          * Fires when the splitter is moved
36471          * @param {Roo.bootstrap.SplitBar} this
36472          * @param {Number} newSize the new width or height
36473          */
36474         "moved" : true,
36475         /**
36476          * @event beforeresize
36477          * Fires before the splitter is dragged
36478          * @param {Roo.bootstrap.SplitBar} this
36479          */
36480         "beforeresize" : true,
36481
36482         "beforeapply" : true
36483     });
36484
36485     Roo.util.Observable.call(this);
36486 };
36487
36488 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36489     onStartProxyDrag : function(x, y){
36490         this.fireEvent("beforeresize", this);
36491         if(!this.overlay){
36492             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36493             o.unselectable();
36494             o.enableDisplayMode("block");
36495             // all splitbars share the same overlay
36496             Roo.bootstrap.SplitBar.prototype.overlay = o;
36497         }
36498         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36499         this.overlay.show();
36500         Roo.get(this.proxy).setDisplayed("block");
36501         var size = this.adapter.getElementSize(this);
36502         this.activeMinSize = this.getMinimumSize();;
36503         this.activeMaxSize = this.getMaximumSize();;
36504         var c1 = size - this.activeMinSize;
36505         var c2 = Math.max(this.activeMaxSize - size, 0);
36506         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36507             this.dd.resetConstraints();
36508             this.dd.setXConstraint(
36509                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36510                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36511             );
36512             this.dd.setYConstraint(0, 0);
36513         }else{
36514             this.dd.resetConstraints();
36515             this.dd.setXConstraint(0, 0);
36516             this.dd.setYConstraint(
36517                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36518                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36519             );
36520          }
36521         this.dragSpecs.startSize = size;
36522         this.dragSpecs.startPoint = [x, y];
36523         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36524     },
36525     
36526     /** 
36527      * @private Called after the drag operation by the DDProxy
36528      */
36529     onEndProxyDrag : function(e){
36530         Roo.get(this.proxy).setDisplayed(false);
36531         var endPoint = Roo.lib.Event.getXY(e);
36532         if(this.overlay){
36533             this.overlay.hide();
36534         }
36535         var newSize;
36536         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36537             newSize = this.dragSpecs.startSize + 
36538                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36539                     endPoint[0] - this.dragSpecs.startPoint[0] :
36540                     this.dragSpecs.startPoint[0] - endPoint[0]
36541                 );
36542         }else{
36543             newSize = this.dragSpecs.startSize + 
36544                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36545                     endPoint[1] - this.dragSpecs.startPoint[1] :
36546                     this.dragSpecs.startPoint[1] - endPoint[1]
36547                 );
36548         }
36549         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36550         if(newSize != this.dragSpecs.startSize){
36551             if(this.fireEvent('beforeapply', this, newSize) !== false){
36552                 this.adapter.setElementSize(this, newSize);
36553                 this.fireEvent("moved", this, newSize);
36554                 this.fireEvent("resize", this, newSize);
36555             }
36556         }
36557     },
36558     
36559     /**
36560      * Get the adapter this SplitBar uses
36561      * @return The adapter object
36562      */
36563     getAdapter : function(){
36564         return this.adapter;
36565     },
36566     
36567     /**
36568      * Set the adapter this SplitBar uses
36569      * @param {Object} adapter A SplitBar adapter object
36570      */
36571     setAdapter : function(adapter){
36572         this.adapter = adapter;
36573         this.adapter.init(this);
36574     },
36575     
36576     /**
36577      * Gets the minimum size for the resizing element
36578      * @return {Number} The minimum size
36579      */
36580     getMinimumSize : function(){
36581         return this.minSize;
36582     },
36583     
36584     /**
36585      * Sets the minimum size for the resizing element
36586      * @param {Number} minSize The minimum size
36587      */
36588     setMinimumSize : function(minSize){
36589         this.minSize = minSize;
36590     },
36591     
36592     /**
36593      * Gets the maximum size for the resizing element
36594      * @return {Number} The maximum size
36595      */
36596     getMaximumSize : function(){
36597         return this.maxSize;
36598     },
36599     
36600     /**
36601      * Sets the maximum size for the resizing element
36602      * @param {Number} maxSize The maximum size
36603      */
36604     setMaximumSize : function(maxSize){
36605         this.maxSize = maxSize;
36606     },
36607     
36608     /**
36609      * Sets the initialize size for the resizing element
36610      * @param {Number} size The initial size
36611      */
36612     setCurrentSize : function(size){
36613         var oldAnimate = this.animate;
36614         this.animate = false;
36615         this.adapter.setElementSize(this, size);
36616         this.animate = oldAnimate;
36617     },
36618     
36619     /**
36620      * Destroy this splitbar. 
36621      * @param {Boolean} removeEl True to remove the element
36622      */
36623     destroy : function(removeEl){
36624         if(this.shim){
36625             this.shim.remove();
36626         }
36627         this.dd.unreg();
36628         this.proxy.parentNode.removeChild(this.proxy);
36629         if(removeEl){
36630             this.el.remove();
36631         }
36632     }
36633 });
36634
36635 /**
36636  * @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.
36637  */
36638 Roo.bootstrap.SplitBar.createProxy = function(dir){
36639     var proxy = new Roo.Element(document.createElement("div"));
36640     proxy.unselectable();
36641     var cls = 'roo-splitbar-proxy';
36642     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36643     document.body.appendChild(proxy.dom);
36644     return proxy.dom;
36645 };
36646
36647 /** 
36648  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36649  * Default Adapter. It assumes the splitter and resizing element are not positioned
36650  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36651  */
36652 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36653 };
36654
36655 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36656     // do nothing for now
36657     init : function(s){
36658     
36659     },
36660     /**
36661      * Called before drag operations to get the current size of the resizing element. 
36662      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36663      */
36664      getElementSize : function(s){
36665         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36666             return s.resizingEl.getWidth();
36667         }else{
36668             return s.resizingEl.getHeight();
36669         }
36670     },
36671     
36672     /**
36673      * Called after drag operations to set the size of the resizing element.
36674      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36675      * @param {Number} newSize The new size to set
36676      * @param {Function} onComplete A function to be invoked when resizing is complete
36677      */
36678     setElementSize : function(s, newSize, onComplete){
36679         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36680             if(!s.animate){
36681                 s.resizingEl.setWidth(newSize);
36682                 if(onComplete){
36683                     onComplete(s, newSize);
36684                 }
36685             }else{
36686                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36687             }
36688         }else{
36689             
36690             if(!s.animate){
36691                 s.resizingEl.setHeight(newSize);
36692                 if(onComplete){
36693                     onComplete(s, newSize);
36694                 }
36695             }else{
36696                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36697             }
36698         }
36699     }
36700 };
36701
36702 /** 
36703  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36704  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36705  * Adapter that  moves the splitter element to align with the resized sizing element. 
36706  * Used with an absolute positioned SplitBar.
36707  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36708  * document.body, make sure you assign an id to the body element.
36709  */
36710 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36711     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36712     this.container = Roo.get(container);
36713 };
36714
36715 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36716     init : function(s){
36717         this.basic.init(s);
36718     },
36719     
36720     getElementSize : function(s){
36721         return this.basic.getElementSize(s);
36722     },
36723     
36724     setElementSize : function(s, newSize, onComplete){
36725         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36726     },
36727     
36728     moveSplitter : function(s){
36729         var yes = Roo.bootstrap.SplitBar;
36730         switch(s.placement){
36731             case yes.LEFT:
36732                 s.el.setX(s.resizingEl.getRight());
36733                 break;
36734             case yes.RIGHT:
36735                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36736                 break;
36737             case yes.TOP:
36738                 s.el.setY(s.resizingEl.getBottom());
36739                 break;
36740             case yes.BOTTOM:
36741                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36742                 break;
36743         }
36744     }
36745 };
36746
36747 /**
36748  * Orientation constant - Create a vertical SplitBar
36749  * @static
36750  * @type Number
36751  */
36752 Roo.bootstrap.SplitBar.VERTICAL = 1;
36753
36754 /**
36755  * Orientation constant - Create a horizontal SplitBar
36756  * @static
36757  * @type Number
36758  */
36759 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36760
36761 /**
36762  * Placement constant - The resizing element is to the left of the splitter element
36763  * @static
36764  * @type Number
36765  */
36766 Roo.bootstrap.SplitBar.LEFT = 1;
36767
36768 /**
36769  * Placement constant - The resizing element is to the right of the splitter element
36770  * @static
36771  * @type Number
36772  */
36773 Roo.bootstrap.SplitBar.RIGHT = 2;
36774
36775 /**
36776  * Placement constant - The resizing element is positioned above the splitter element
36777  * @static
36778  * @type Number
36779  */
36780 Roo.bootstrap.SplitBar.TOP = 3;
36781
36782 /**
36783  * Placement constant - The resizing element is positioned under splitter element
36784  * @static
36785  * @type Number
36786  */
36787 Roo.bootstrap.SplitBar.BOTTOM = 4;
36788 Roo.namespace("Roo.bootstrap.layout");/*
36789  * Based on:
36790  * Ext JS Library 1.1.1
36791  * Copyright(c) 2006-2007, Ext JS, LLC.
36792  *
36793  * Originally Released Under LGPL - original licence link has changed is not relivant.
36794  *
36795  * Fork - LGPL
36796  * <script type="text/javascript">
36797  */
36798
36799 /**
36800  * @class Roo.bootstrap.layout.Manager
36801  * @extends Roo.bootstrap.Component
36802  * Base class for layout managers.
36803  */
36804 Roo.bootstrap.layout.Manager = function(config)
36805 {
36806     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36807
36808
36809
36810
36811
36812     /** false to disable window resize monitoring @type Boolean */
36813     this.monitorWindowResize = true;
36814     this.regions = {};
36815     this.addEvents({
36816         /**
36817          * @event layout
36818          * Fires when a layout is performed.
36819          * @param {Roo.LayoutManager} this
36820          */
36821         "layout" : true,
36822         /**
36823          * @event regionresized
36824          * Fires when the user resizes a region.
36825          * @param {Roo.LayoutRegion} region The resized region
36826          * @param {Number} newSize The new size (width for east/west, height for north/south)
36827          */
36828         "regionresized" : true,
36829         /**
36830          * @event regioncollapsed
36831          * Fires when a region is collapsed.
36832          * @param {Roo.LayoutRegion} region The collapsed region
36833          */
36834         "regioncollapsed" : true,
36835         /**
36836          * @event regionexpanded
36837          * Fires when a region is expanded.
36838          * @param {Roo.LayoutRegion} region The expanded region
36839          */
36840         "regionexpanded" : true
36841     });
36842     this.updating = false;
36843
36844     if (config.el) {
36845         this.el = Roo.get(config.el);
36846         this.initEvents();
36847     }
36848
36849 };
36850
36851 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36852
36853
36854     regions : null,
36855
36856     monitorWindowResize : true,
36857
36858
36859     updating : false,
36860
36861
36862     onRender : function(ct, position)
36863     {
36864         if(!this.el){
36865             this.el = Roo.get(ct);
36866             this.initEvents();
36867         }
36868         //this.fireEvent('render',this);
36869     },
36870
36871
36872     initEvents: function()
36873     {
36874
36875
36876         // ie scrollbar fix
36877         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36878             document.body.scroll = "no";
36879         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36880             this.el.position('relative');
36881         }
36882         this.id = this.el.id;
36883         this.el.addClass("roo-layout-container");
36884         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36885         if(this.el.dom != document.body ) {
36886             this.el.on('resize', this.layout,this);
36887             this.el.on('show', this.layout,this);
36888         }
36889
36890     },
36891
36892     /**
36893      * Returns true if this layout is currently being updated
36894      * @return {Boolean}
36895      */
36896     isUpdating : function(){
36897         return this.updating;
36898     },
36899
36900     /**
36901      * Suspend the LayoutManager from doing auto-layouts while
36902      * making multiple add or remove calls
36903      */
36904     beginUpdate : function(){
36905         this.updating = true;
36906     },
36907
36908     /**
36909      * Restore auto-layouts and optionally disable the manager from performing a layout
36910      * @param {Boolean} noLayout true to disable a layout update
36911      */
36912     endUpdate : function(noLayout){
36913         this.updating = false;
36914         if(!noLayout){
36915             this.layout();
36916         }
36917     },
36918
36919     layout: function(){
36920         // abstract...
36921     },
36922
36923     onRegionResized : function(region, newSize){
36924         this.fireEvent("regionresized", region, newSize);
36925         this.layout();
36926     },
36927
36928     onRegionCollapsed : function(region){
36929         this.fireEvent("regioncollapsed", region);
36930     },
36931
36932     onRegionExpanded : function(region){
36933         this.fireEvent("regionexpanded", region);
36934     },
36935
36936     /**
36937      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36938      * performs box-model adjustments.
36939      * @return {Object} The size as an object {width: (the width), height: (the height)}
36940      */
36941     getViewSize : function()
36942     {
36943         var size;
36944         if(this.el.dom != document.body){
36945             size = this.el.getSize();
36946         }else{
36947             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36948         }
36949         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36950         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36951         return size;
36952     },
36953
36954     /**
36955      * Returns the Element this layout is bound to.
36956      * @return {Roo.Element}
36957      */
36958     getEl : function(){
36959         return this.el;
36960     },
36961
36962     /**
36963      * Returns the specified region.
36964      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36965      * @return {Roo.LayoutRegion}
36966      */
36967     getRegion : function(target){
36968         return this.regions[target.toLowerCase()];
36969     },
36970
36971     onWindowResize : function(){
36972         if(this.monitorWindowResize){
36973             this.layout();
36974         }
36975     }
36976 });
36977 /*
36978  * Based on:
36979  * Ext JS Library 1.1.1
36980  * Copyright(c) 2006-2007, Ext JS, LLC.
36981  *
36982  * Originally Released Under LGPL - original licence link has changed is not relivant.
36983  *
36984  * Fork - LGPL
36985  * <script type="text/javascript">
36986  */
36987 /**
36988  * @class Roo.bootstrap.layout.Border
36989  * @extends Roo.bootstrap.layout.Manager
36990  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36991  * please see: examples/bootstrap/nested.html<br><br>
36992  
36993 <b>The container the layout is rendered into can be either the body element or any other element.
36994 If it is not the body element, the container needs to either be an absolute positioned element,
36995 or you will need to add "position:relative" to the css of the container.  You will also need to specify
36996 the container size if it is not the body element.</b>
36997
36998 * @constructor
36999 * Create a new Border
37000 * @param {Object} config Configuration options
37001  */
37002 Roo.bootstrap.layout.Border = function(config){
37003     config = config || {};
37004     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37005     
37006     
37007     
37008     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37009         if(config[region]){
37010             config[region].region = region;
37011             this.addRegion(config[region]);
37012         }
37013     },this);
37014     
37015 };
37016
37017 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37018
37019 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37020     
37021     parent : false, // this might point to a 'nest' or a ???
37022     
37023     /**
37024      * Creates and adds a new region if it doesn't already exist.
37025      * @param {String} target The target region key (north, south, east, west or center).
37026      * @param {Object} config The regions config object
37027      * @return {BorderLayoutRegion} The new region
37028      */
37029     addRegion : function(config)
37030     {
37031         if(!this.regions[config.region]){
37032             var r = this.factory(config);
37033             this.bindRegion(r);
37034         }
37035         return this.regions[config.region];
37036     },
37037
37038     // private (kinda)
37039     bindRegion : function(r){
37040         this.regions[r.config.region] = r;
37041         
37042         r.on("visibilitychange",    this.layout, this);
37043         r.on("paneladded",          this.layout, this);
37044         r.on("panelremoved",        this.layout, this);
37045         r.on("invalidated",         this.layout, this);
37046         r.on("resized",             this.onRegionResized, this);
37047         r.on("collapsed",           this.onRegionCollapsed, this);
37048         r.on("expanded",            this.onRegionExpanded, this);
37049     },
37050
37051     /**
37052      * Performs a layout update.
37053      */
37054     layout : function()
37055     {
37056         if(this.updating) {
37057             return;
37058         }
37059         
37060         // render all the rebions if they have not been done alreayd?
37061         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37062             if(this.regions[region] && !this.regions[region].bodyEl){
37063                 this.regions[region].onRender(this.el)
37064             }
37065         },this);
37066         
37067         var size = this.getViewSize();
37068         var w = size.width;
37069         var h = size.height;
37070         var centerW = w;
37071         var centerH = h;
37072         var centerY = 0;
37073         var centerX = 0;
37074         //var x = 0, y = 0;
37075
37076         var rs = this.regions;
37077         var north = rs["north"];
37078         var south = rs["south"]; 
37079         var west = rs["west"];
37080         var east = rs["east"];
37081         var center = rs["center"];
37082         //if(this.hideOnLayout){ // not supported anymore
37083             //c.el.setStyle("display", "none");
37084         //}
37085         if(north && north.isVisible()){
37086             var b = north.getBox();
37087             var m = north.getMargins();
37088             b.width = w - (m.left+m.right);
37089             b.x = m.left;
37090             b.y = m.top;
37091             centerY = b.height + b.y + m.bottom;
37092             centerH -= centerY;
37093             north.updateBox(this.safeBox(b));
37094         }
37095         if(south && south.isVisible()){
37096             var b = south.getBox();
37097             var m = south.getMargins();
37098             b.width = w - (m.left+m.right);
37099             b.x = m.left;
37100             var totalHeight = (b.height + m.top + m.bottom);
37101             b.y = h - totalHeight + m.top;
37102             centerH -= totalHeight;
37103             south.updateBox(this.safeBox(b));
37104         }
37105         if(west && west.isVisible()){
37106             var b = west.getBox();
37107             var m = west.getMargins();
37108             b.height = centerH - (m.top+m.bottom);
37109             b.x = m.left;
37110             b.y = centerY + m.top;
37111             var totalWidth = (b.width + m.left + m.right);
37112             centerX += totalWidth;
37113             centerW -= totalWidth;
37114             west.updateBox(this.safeBox(b));
37115         }
37116         if(east && east.isVisible()){
37117             var b = east.getBox();
37118             var m = east.getMargins();
37119             b.height = centerH - (m.top+m.bottom);
37120             var totalWidth = (b.width + m.left + m.right);
37121             b.x = w - totalWidth + m.left;
37122             b.y = centerY + m.top;
37123             centerW -= totalWidth;
37124             east.updateBox(this.safeBox(b));
37125         }
37126         if(center){
37127             var m = center.getMargins();
37128             var centerBox = {
37129                 x: centerX + m.left,
37130                 y: centerY + m.top,
37131                 width: centerW - (m.left+m.right),
37132                 height: centerH - (m.top+m.bottom)
37133             };
37134             //if(this.hideOnLayout){
37135                 //center.el.setStyle("display", "block");
37136             //}
37137             center.updateBox(this.safeBox(centerBox));
37138         }
37139         this.el.repaint();
37140         this.fireEvent("layout", this);
37141     },
37142
37143     // private
37144     safeBox : function(box){
37145         box.width = Math.max(0, box.width);
37146         box.height = Math.max(0, box.height);
37147         return box;
37148     },
37149
37150     /**
37151      * Adds a ContentPanel (or subclass) to this layout.
37152      * @param {String} target The target region key (north, south, east, west or center).
37153      * @param {Roo.ContentPanel} panel The panel to add
37154      * @return {Roo.ContentPanel} The added panel
37155      */
37156     add : function(target, panel){
37157          
37158         target = target.toLowerCase();
37159         return this.regions[target].add(panel);
37160     },
37161
37162     /**
37163      * Remove a ContentPanel (or subclass) to this layout.
37164      * @param {String} target The target region key (north, south, east, west or center).
37165      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37166      * @return {Roo.ContentPanel} The removed panel
37167      */
37168     remove : function(target, panel){
37169         target = target.toLowerCase();
37170         return this.regions[target].remove(panel);
37171     },
37172
37173     /**
37174      * Searches all regions for a panel with the specified id
37175      * @param {String} panelId
37176      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37177      */
37178     findPanel : function(panelId){
37179         var rs = this.regions;
37180         for(var target in rs){
37181             if(typeof rs[target] != "function"){
37182                 var p = rs[target].getPanel(panelId);
37183                 if(p){
37184                     return p;
37185                 }
37186             }
37187         }
37188         return null;
37189     },
37190
37191     /**
37192      * Searches all regions for a panel with the specified id and activates (shows) it.
37193      * @param {String/ContentPanel} panelId The panels id or the panel itself
37194      * @return {Roo.ContentPanel} The shown panel or null
37195      */
37196     showPanel : function(panelId) {
37197       var rs = this.regions;
37198       for(var target in rs){
37199          var r = rs[target];
37200          if(typeof r != "function"){
37201             if(r.hasPanel(panelId)){
37202                return r.showPanel(panelId);
37203             }
37204          }
37205       }
37206       return null;
37207    },
37208
37209    /**
37210      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37211      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37212      */
37213    /*
37214     restoreState : function(provider){
37215         if(!provider){
37216             provider = Roo.state.Manager;
37217         }
37218         var sm = new Roo.LayoutStateManager();
37219         sm.init(this, provider);
37220     },
37221 */
37222  
37223  
37224     /**
37225      * Adds a xtype elements to the layout.
37226      * <pre><code>
37227
37228 layout.addxtype({
37229        xtype : 'ContentPanel',
37230        region: 'west',
37231        items: [ .... ]
37232    }
37233 );
37234
37235 layout.addxtype({
37236         xtype : 'NestedLayoutPanel',
37237         region: 'west',
37238         layout: {
37239            center: { },
37240            west: { }   
37241         },
37242         items : [ ... list of content panels or nested layout panels.. ]
37243    }
37244 );
37245 </code></pre>
37246      * @param {Object} cfg Xtype definition of item to add.
37247      */
37248     addxtype : function(cfg)
37249     {
37250         // basically accepts a pannel...
37251         // can accept a layout region..!?!?
37252         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37253         
37254         
37255         // theory?  children can only be panels??
37256         
37257         //if (!cfg.xtype.match(/Panel$/)) {
37258         //    return false;
37259         //}
37260         var ret = false;
37261         
37262         if (typeof(cfg.region) == 'undefined') {
37263             Roo.log("Failed to add Panel, region was not set");
37264             Roo.log(cfg);
37265             return false;
37266         }
37267         var region = cfg.region;
37268         delete cfg.region;
37269         
37270           
37271         var xitems = [];
37272         if (cfg.items) {
37273             xitems = cfg.items;
37274             delete cfg.items;
37275         }
37276         var nb = false;
37277         
37278         if ( region == 'center') {
37279             Roo.log("Center: " + cfg.title);
37280         }
37281         
37282         
37283         switch(cfg.xtype) 
37284         {
37285             case 'Content':  // ContentPanel (el, cfg)
37286             case 'Scroll':  // ContentPanel (el, cfg)
37287             case 'View': 
37288                 cfg.autoCreate = cfg.autoCreate || true;
37289                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37290                 //} else {
37291                 //    var el = this.el.createChild();
37292                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37293                 //}
37294                 
37295                 this.add(region, ret);
37296                 break;
37297             
37298             /*
37299             case 'TreePanel': // our new panel!
37300                 cfg.el = this.el.createChild();
37301                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37302                 this.add(region, ret);
37303                 break;
37304             */
37305             
37306             case 'Nest': 
37307                 // create a new Layout (which is  a Border Layout...
37308                 
37309                 var clayout = cfg.layout;
37310                 clayout.el  = this.el.createChild();
37311                 clayout.items   = clayout.items  || [];
37312                 
37313                 delete cfg.layout;
37314                 
37315                 // replace this exitems with the clayout ones..
37316                 xitems = clayout.items;
37317                  
37318                 // force background off if it's in center...
37319                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37320                     cfg.background = false;
37321                 }
37322                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37323                 
37324                 
37325                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37326                 //console.log('adding nested layout panel '  + cfg.toSource());
37327                 this.add(region, ret);
37328                 nb = {}; /// find first...
37329                 break;
37330             
37331             case 'Grid':
37332                 
37333                 // needs grid and region
37334                 
37335                 //var el = this.getRegion(region).el.createChild();
37336                 /*
37337                  *var el = this.el.createChild();
37338                 // create the grid first...
37339                 cfg.grid.container = el;
37340                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37341                 */
37342                 
37343                 if (region == 'center' && this.active ) {
37344                     cfg.background = false;
37345                 }
37346                 
37347                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37348                 
37349                 this.add(region, ret);
37350                 /*
37351                 if (cfg.background) {
37352                     // render grid on panel activation (if panel background)
37353                     ret.on('activate', function(gp) {
37354                         if (!gp.grid.rendered) {
37355                     //        gp.grid.render(el);
37356                         }
37357                     });
37358                 } else {
37359                   //  cfg.grid.render(el);
37360                 }
37361                 */
37362                 break;
37363            
37364            
37365             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37366                 // it was the old xcomponent building that caused this before.
37367                 // espeically if border is the top element in the tree.
37368                 ret = this;
37369                 break; 
37370                 
37371                     
37372                 
37373                 
37374                 
37375             default:
37376                 /*
37377                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37378                     
37379                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37380                     this.add(region, ret);
37381                 } else {
37382                 */
37383                     Roo.log(cfg);
37384                     throw "Can not add '" + cfg.xtype + "' to Border";
37385                     return null;
37386              
37387                                 
37388              
37389         }
37390         this.beginUpdate();
37391         // add children..
37392         var region = '';
37393         var abn = {};
37394         Roo.each(xitems, function(i)  {
37395             region = nb && i.region ? i.region : false;
37396             
37397             var add = ret.addxtype(i);
37398            
37399             if (region) {
37400                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37401                 if (!i.background) {
37402                     abn[region] = nb[region] ;
37403                 }
37404             }
37405             
37406         });
37407         this.endUpdate();
37408
37409         // make the last non-background panel active..
37410         //if (nb) { Roo.log(abn); }
37411         if (nb) {
37412             
37413             for(var r in abn) {
37414                 region = this.getRegion(r);
37415                 if (region) {
37416                     // tried using nb[r], but it does not work..
37417                      
37418                     region.showPanel(abn[r]);
37419                    
37420                 }
37421             }
37422         }
37423         return ret;
37424         
37425     },
37426     
37427     
37428 // private
37429     factory : function(cfg)
37430     {
37431         
37432         var validRegions = Roo.bootstrap.layout.Border.regions;
37433
37434         var target = cfg.region;
37435         cfg.mgr = this;
37436         
37437         var r = Roo.bootstrap.layout;
37438         Roo.log(target);
37439         switch(target){
37440             case "north":
37441                 return new r.North(cfg);
37442             case "south":
37443                 return new r.South(cfg);
37444             case "east":
37445                 return new r.East(cfg);
37446             case "west":
37447                 return new r.West(cfg);
37448             case "center":
37449                 return new r.Center(cfg);
37450         }
37451         throw 'Layout region "'+target+'" not supported.';
37452     }
37453     
37454     
37455 });
37456  /*
37457  * Based on:
37458  * Ext JS Library 1.1.1
37459  * Copyright(c) 2006-2007, Ext JS, LLC.
37460  *
37461  * Originally Released Under LGPL - original licence link has changed is not relivant.
37462  *
37463  * Fork - LGPL
37464  * <script type="text/javascript">
37465  */
37466  
37467 /**
37468  * @class Roo.bootstrap.layout.Basic
37469  * @extends Roo.util.Observable
37470  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37471  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37472  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37473  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37474  * @cfg {string}   region  the region that it inhabits..
37475  * @cfg {bool}   skipConfig skip config?
37476  * 
37477
37478  */
37479 Roo.bootstrap.layout.Basic = function(config){
37480     
37481     this.mgr = config.mgr;
37482     
37483     this.position = config.region;
37484     
37485     var skipConfig = config.skipConfig;
37486     
37487     this.events = {
37488         /**
37489          * @scope Roo.BasicLayoutRegion
37490          */
37491         
37492         /**
37493          * @event beforeremove
37494          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37495          * @param {Roo.LayoutRegion} this
37496          * @param {Roo.ContentPanel} panel The panel
37497          * @param {Object} e The cancel event object
37498          */
37499         "beforeremove" : true,
37500         /**
37501          * @event invalidated
37502          * Fires when the layout for this region is changed.
37503          * @param {Roo.LayoutRegion} this
37504          */
37505         "invalidated" : true,
37506         /**
37507          * @event visibilitychange
37508          * Fires when this region is shown or hidden 
37509          * @param {Roo.LayoutRegion} this
37510          * @param {Boolean} visibility true or false
37511          */
37512         "visibilitychange" : true,
37513         /**
37514          * @event paneladded
37515          * Fires when a panel is added. 
37516          * @param {Roo.LayoutRegion} this
37517          * @param {Roo.ContentPanel} panel The panel
37518          */
37519         "paneladded" : true,
37520         /**
37521          * @event panelremoved
37522          * Fires when a panel is removed. 
37523          * @param {Roo.LayoutRegion} this
37524          * @param {Roo.ContentPanel} panel The panel
37525          */
37526         "panelremoved" : true,
37527         /**
37528          * @event beforecollapse
37529          * Fires when this region before collapse.
37530          * @param {Roo.LayoutRegion} this
37531          */
37532         "beforecollapse" : true,
37533         /**
37534          * @event collapsed
37535          * Fires when this region is collapsed.
37536          * @param {Roo.LayoutRegion} this
37537          */
37538         "collapsed" : true,
37539         /**
37540          * @event expanded
37541          * Fires when this region is expanded.
37542          * @param {Roo.LayoutRegion} this
37543          */
37544         "expanded" : true,
37545         /**
37546          * @event slideshow
37547          * Fires when this region is slid into view.
37548          * @param {Roo.LayoutRegion} this
37549          */
37550         "slideshow" : true,
37551         /**
37552          * @event slidehide
37553          * Fires when this region slides out of view. 
37554          * @param {Roo.LayoutRegion} this
37555          */
37556         "slidehide" : true,
37557         /**
37558          * @event panelactivated
37559          * Fires when a panel is activated. 
37560          * @param {Roo.LayoutRegion} this
37561          * @param {Roo.ContentPanel} panel The activated panel
37562          */
37563         "panelactivated" : true,
37564         /**
37565          * @event resized
37566          * Fires when the user resizes this region. 
37567          * @param {Roo.LayoutRegion} this
37568          * @param {Number} newSize The new size (width for east/west, height for north/south)
37569          */
37570         "resized" : true
37571     };
37572     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37573     this.panels = new Roo.util.MixedCollection();
37574     this.panels.getKey = this.getPanelId.createDelegate(this);
37575     this.box = null;
37576     this.activePanel = null;
37577     // ensure listeners are added...
37578     
37579     if (config.listeners || config.events) {
37580         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37581             listeners : config.listeners || {},
37582             events : config.events || {}
37583         });
37584     }
37585     
37586     if(skipConfig !== true){
37587         this.applyConfig(config);
37588     }
37589 };
37590
37591 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37592 {
37593     getPanelId : function(p){
37594         return p.getId();
37595     },
37596     
37597     applyConfig : function(config){
37598         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37599         this.config = config;
37600         
37601     },
37602     
37603     /**
37604      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37605      * the width, for horizontal (north, south) the height.
37606      * @param {Number} newSize The new width or height
37607      */
37608     resizeTo : function(newSize){
37609         var el = this.el ? this.el :
37610                  (this.activePanel ? this.activePanel.getEl() : null);
37611         if(el){
37612             switch(this.position){
37613                 case "east":
37614                 case "west":
37615                     el.setWidth(newSize);
37616                     this.fireEvent("resized", this, newSize);
37617                 break;
37618                 case "north":
37619                 case "south":
37620                     el.setHeight(newSize);
37621                     this.fireEvent("resized", this, newSize);
37622                 break;                
37623             }
37624         }
37625     },
37626     
37627     getBox : function(){
37628         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37629     },
37630     
37631     getMargins : function(){
37632         return this.margins;
37633     },
37634     
37635     updateBox : function(box){
37636         this.box = box;
37637         var el = this.activePanel.getEl();
37638         el.dom.style.left = box.x + "px";
37639         el.dom.style.top = box.y + "px";
37640         this.activePanel.setSize(box.width, box.height);
37641     },
37642     
37643     /**
37644      * Returns the container element for this region.
37645      * @return {Roo.Element}
37646      */
37647     getEl : function(){
37648         return this.activePanel;
37649     },
37650     
37651     /**
37652      * Returns true if this region is currently visible.
37653      * @return {Boolean}
37654      */
37655     isVisible : function(){
37656         return this.activePanel ? true : false;
37657     },
37658     
37659     setActivePanel : function(panel){
37660         panel = this.getPanel(panel);
37661         if(this.activePanel && this.activePanel != panel){
37662             this.activePanel.setActiveState(false);
37663             this.activePanel.getEl().setLeftTop(-10000,-10000);
37664         }
37665         this.activePanel = panel;
37666         panel.setActiveState(true);
37667         if(this.box){
37668             panel.setSize(this.box.width, this.box.height);
37669         }
37670         this.fireEvent("panelactivated", this, panel);
37671         this.fireEvent("invalidated");
37672     },
37673     
37674     /**
37675      * Show the specified panel.
37676      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37677      * @return {Roo.ContentPanel} The shown panel or null
37678      */
37679     showPanel : function(panel){
37680         panel = this.getPanel(panel);
37681         if(panel){
37682             this.setActivePanel(panel);
37683         }
37684         return panel;
37685     },
37686     
37687     /**
37688      * Get the active panel for this region.
37689      * @return {Roo.ContentPanel} The active panel or null
37690      */
37691     getActivePanel : function(){
37692         return this.activePanel;
37693     },
37694     
37695     /**
37696      * Add the passed ContentPanel(s)
37697      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37698      * @return {Roo.ContentPanel} The panel added (if only one was added)
37699      */
37700     add : function(panel){
37701         if(arguments.length > 1){
37702             for(var i = 0, len = arguments.length; i < len; i++) {
37703                 this.add(arguments[i]);
37704             }
37705             return null;
37706         }
37707         if(this.hasPanel(panel)){
37708             this.showPanel(panel);
37709             return panel;
37710         }
37711         var el = panel.getEl();
37712         if(el.dom.parentNode != this.mgr.el.dom){
37713             this.mgr.el.dom.appendChild(el.dom);
37714         }
37715         if(panel.setRegion){
37716             panel.setRegion(this);
37717         }
37718         this.panels.add(panel);
37719         el.setStyle("position", "absolute");
37720         if(!panel.background){
37721             this.setActivePanel(panel);
37722             if(this.config.initialSize && this.panels.getCount()==1){
37723                 this.resizeTo(this.config.initialSize);
37724             }
37725         }
37726         this.fireEvent("paneladded", this, panel);
37727         return panel;
37728     },
37729     
37730     /**
37731      * Returns true if the panel is in this region.
37732      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37733      * @return {Boolean}
37734      */
37735     hasPanel : function(panel){
37736         if(typeof panel == "object"){ // must be panel obj
37737             panel = panel.getId();
37738         }
37739         return this.getPanel(panel) ? true : false;
37740     },
37741     
37742     /**
37743      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37744      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37745      * @param {Boolean} preservePanel Overrides the config preservePanel option
37746      * @return {Roo.ContentPanel} The panel that was removed
37747      */
37748     remove : function(panel, preservePanel){
37749         panel = this.getPanel(panel);
37750         if(!panel){
37751             return null;
37752         }
37753         var e = {};
37754         this.fireEvent("beforeremove", this, panel, e);
37755         if(e.cancel === true){
37756             return null;
37757         }
37758         var panelId = panel.getId();
37759         this.panels.removeKey(panelId);
37760         return panel;
37761     },
37762     
37763     /**
37764      * Returns the panel specified or null if it's not in this region.
37765      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37766      * @return {Roo.ContentPanel}
37767      */
37768     getPanel : function(id){
37769         if(typeof id == "object"){ // must be panel obj
37770             return id;
37771         }
37772         return this.panels.get(id);
37773     },
37774     
37775     /**
37776      * Returns this regions position (north/south/east/west/center).
37777      * @return {String} 
37778      */
37779     getPosition: function(){
37780         return this.position;    
37781     }
37782 });/*
37783  * Based on:
37784  * Ext JS Library 1.1.1
37785  * Copyright(c) 2006-2007, Ext JS, LLC.
37786  *
37787  * Originally Released Under LGPL - original licence link has changed is not relivant.
37788  *
37789  * Fork - LGPL
37790  * <script type="text/javascript">
37791  */
37792  
37793 /**
37794  * @class Roo.bootstrap.layout.Region
37795  * @extends Roo.bootstrap.layout.Basic
37796  * This class represents a region in a layout manager.
37797  
37798  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37799  * @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})
37800  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37801  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37802  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37803  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37804  * @cfg {String}    title           The title for the region (overrides panel titles)
37805  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37806  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37807  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37808  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37809  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37810  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37811  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37812  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37813  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37814  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37815
37816  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37817  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37818  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37819  * @cfg {Number}    width           For East/West panels
37820  * @cfg {Number}    height          For North/South panels
37821  * @cfg {Boolean}   split           To show the splitter
37822  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37823  * 
37824  * @cfg {string}   cls             Extra CSS classes to add to region
37825  * 
37826  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37827  * @cfg {string}   region  the region that it inhabits..
37828  *
37829
37830  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37831  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37832
37833  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37834  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37835  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37836  */
37837 Roo.bootstrap.layout.Region = function(config)
37838 {
37839     this.applyConfig(config);
37840
37841     var mgr = config.mgr;
37842     var pos = config.region;
37843     config.skipConfig = true;
37844     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37845     
37846     if (mgr.el) {
37847         this.onRender(mgr.el);   
37848     }
37849      
37850     this.visible = true;
37851     this.collapsed = false;
37852     this.unrendered_panels = [];
37853 };
37854
37855 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37856
37857     position: '', // set by wrapper (eg. north/south etc..)
37858     unrendered_panels : null,  // unrendered panels.
37859     
37860     tabPosition : false,
37861     
37862     mgr: false, // points to 'Border'
37863     
37864     
37865     createBody : function(){
37866         /** This region's body element 
37867         * @type Roo.Element */
37868         this.bodyEl = this.el.createChild({
37869                 tag: "div",
37870                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37871         });
37872     },
37873
37874     onRender: function(ctr, pos)
37875     {
37876         var dh = Roo.DomHelper;
37877         /** This region's container element 
37878         * @type Roo.Element */
37879         this.el = dh.append(ctr.dom, {
37880                 tag: "div",
37881                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37882             }, true);
37883         /** This region's title element 
37884         * @type Roo.Element */
37885     
37886         this.titleEl = dh.append(this.el.dom,  {
37887                 tag: "div",
37888                 unselectable: "on",
37889                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37890                 children:[
37891                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37892                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37893                 ]
37894             }, true);
37895         
37896         this.titleEl.enableDisplayMode();
37897         /** This region's title text element 
37898         * @type HTMLElement */
37899         this.titleTextEl = this.titleEl.dom.firstChild;
37900         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37901         /*
37902         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37903         this.closeBtn.enableDisplayMode();
37904         this.closeBtn.on("click", this.closeClicked, this);
37905         this.closeBtn.hide();
37906     */
37907         this.createBody(this.config);
37908         if(this.config.hideWhenEmpty){
37909             this.hide();
37910             this.on("paneladded", this.validateVisibility, this);
37911             this.on("panelremoved", this.validateVisibility, this);
37912         }
37913         if(this.autoScroll){
37914             this.bodyEl.setStyle("overflow", "auto");
37915         }else{
37916             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37917         }
37918         //if(c.titlebar !== false){
37919             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37920                 this.titleEl.hide();
37921             }else{
37922                 this.titleEl.show();
37923                 if(this.config.title){
37924                     this.titleTextEl.innerHTML = this.config.title;
37925                 }
37926             }
37927         //}
37928         if(this.config.collapsed){
37929             this.collapse(true);
37930         }
37931         if(this.config.hidden){
37932             this.hide();
37933         }
37934         
37935         if (this.unrendered_panels && this.unrendered_panels.length) {
37936             for (var i =0;i< this.unrendered_panels.length; i++) {
37937                 this.add(this.unrendered_panels[i]);
37938             }
37939             this.unrendered_panels = null;
37940             
37941         }
37942         
37943     },
37944     
37945     applyConfig : function(c)
37946     {
37947         /*
37948          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37949             var dh = Roo.DomHelper;
37950             if(c.titlebar !== false){
37951                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37952                 this.collapseBtn.on("click", this.collapse, this);
37953                 this.collapseBtn.enableDisplayMode();
37954                 /*
37955                 if(c.showPin === true || this.showPin){
37956                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37957                     this.stickBtn.enableDisplayMode();
37958                     this.stickBtn.on("click", this.expand, this);
37959                     this.stickBtn.hide();
37960                 }
37961                 
37962             }
37963             */
37964             /** This region's collapsed element
37965             * @type Roo.Element */
37966             /*
37967              *
37968             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37969                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37970             ]}, true);
37971             
37972             if(c.floatable !== false){
37973                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37974                this.collapsedEl.on("click", this.collapseClick, this);
37975             }
37976
37977             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37978                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37979                    id: "message", unselectable: "on", style:{"float":"left"}});
37980                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37981              }
37982             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37983             this.expandBtn.on("click", this.expand, this);
37984             
37985         }
37986         
37987         if(this.collapseBtn){
37988             this.collapseBtn.setVisible(c.collapsible == true);
37989         }
37990         
37991         this.cmargins = c.cmargins || this.cmargins ||
37992                          (this.position == "west" || this.position == "east" ?
37993                              {top: 0, left: 2, right:2, bottom: 0} :
37994                              {top: 2, left: 0, right:0, bottom: 2});
37995         */
37996         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37997         
37998         
37999         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38000         
38001         this.autoScroll = c.autoScroll || false;
38002         
38003         
38004        
38005         
38006         this.duration = c.duration || .30;
38007         this.slideDuration = c.slideDuration || .45;
38008         this.config = c;
38009        
38010     },
38011     /**
38012      * Returns true if this region is currently visible.
38013      * @return {Boolean}
38014      */
38015     isVisible : function(){
38016         return this.visible;
38017     },
38018
38019     /**
38020      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38021      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38022      */
38023     //setCollapsedTitle : function(title){
38024     //    title = title || "&#160;";
38025      //   if(this.collapsedTitleTextEl){
38026       //      this.collapsedTitleTextEl.innerHTML = title;
38027        // }
38028     //},
38029
38030     getBox : function(){
38031         var b;
38032       //  if(!this.collapsed){
38033             b = this.el.getBox(false, true);
38034        // }else{
38035           //  b = this.collapsedEl.getBox(false, true);
38036         //}
38037         return b;
38038     },
38039
38040     getMargins : function(){
38041         return this.margins;
38042         //return this.collapsed ? this.cmargins : this.margins;
38043     },
38044 /*
38045     highlight : function(){
38046         this.el.addClass("x-layout-panel-dragover");
38047     },
38048
38049     unhighlight : function(){
38050         this.el.removeClass("x-layout-panel-dragover");
38051     },
38052 */
38053     updateBox : function(box)
38054     {
38055         if (!this.bodyEl) {
38056             return; // not rendered yet..
38057         }
38058         
38059         this.box = box;
38060         if(!this.collapsed){
38061             this.el.dom.style.left = box.x + "px";
38062             this.el.dom.style.top = box.y + "px";
38063             this.updateBody(box.width, box.height);
38064         }else{
38065             this.collapsedEl.dom.style.left = box.x + "px";
38066             this.collapsedEl.dom.style.top = box.y + "px";
38067             this.collapsedEl.setSize(box.width, box.height);
38068         }
38069         if(this.tabs){
38070             this.tabs.autoSizeTabs();
38071         }
38072     },
38073
38074     updateBody : function(w, h)
38075     {
38076         if(w !== null){
38077             this.el.setWidth(w);
38078             w -= this.el.getBorderWidth("rl");
38079             if(this.config.adjustments){
38080                 w += this.config.adjustments[0];
38081             }
38082         }
38083         if(h !== null && h > 0){
38084             this.el.setHeight(h);
38085             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38086             h -= this.el.getBorderWidth("tb");
38087             if(this.config.adjustments){
38088                 h += this.config.adjustments[1];
38089             }
38090             this.bodyEl.setHeight(h);
38091             if(this.tabs){
38092                 h = this.tabs.syncHeight(h);
38093             }
38094         }
38095         if(this.panelSize){
38096             w = w !== null ? w : this.panelSize.width;
38097             h = h !== null ? h : this.panelSize.height;
38098         }
38099         if(this.activePanel){
38100             var el = this.activePanel.getEl();
38101             w = w !== null ? w : el.getWidth();
38102             h = h !== null ? h : el.getHeight();
38103             this.panelSize = {width: w, height: h};
38104             this.activePanel.setSize(w, h);
38105         }
38106         if(Roo.isIE && this.tabs){
38107             this.tabs.el.repaint();
38108         }
38109     },
38110
38111     /**
38112      * Returns the container element for this region.
38113      * @return {Roo.Element}
38114      */
38115     getEl : function(){
38116         return this.el;
38117     },
38118
38119     /**
38120      * Hides this region.
38121      */
38122     hide : function(){
38123         //if(!this.collapsed){
38124             this.el.dom.style.left = "-2000px";
38125             this.el.hide();
38126         //}else{
38127          //   this.collapsedEl.dom.style.left = "-2000px";
38128          //   this.collapsedEl.hide();
38129        // }
38130         this.visible = false;
38131         this.fireEvent("visibilitychange", this, false);
38132     },
38133
38134     /**
38135      * Shows this region if it was previously hidden.
38136      */
38137     show : function(){
38138         //if(!this.collapsed){
38139             this.el.show();
38140         //}else{
38141         //    this.collapsedEl.show();
38142        // }
38143         this.visible = true;
38144         this.fireEvent("visibilitychange", this, true);
38145     },
38146 /*
38147     closeClicked : function(){
38148         if(this.activePanel){
38149             this.remove(this.activePanel);
38150         }
38151     },
38152
38153     collapseClick : function(e){
38154         if(this.isSlid){
38155            e.stopPropagation();
38156            this.slideIn();
38157         }else{
38158            e.stopPropagation();
38159            this.slideOut();
38160         }
38161     },
38162 */
38163     /**
38164      * Collapses this region.
38165      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38166      */
38167     /*
38168     collapse : function(skipAnim, skipCheck = false){
38169         if(this.collapsed) {
38170             return;
38171         }
38172         
38173         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38174             
38175             this.collapsed = true;
38176             if(this.split){
38177                 this.split.el.hide();
38178             }
38179             if(this.config.animate && skipAnim !== true){
38180                 this.fireEvent("invalidated", this);
38181                 this.animateCollapse();
38182             }else{
38183                 this.el.setLocation(-20000,-20000);
38184                 this.el.hide();
38185                 this.collapsedEl.show();
38186                 this.fireEvent("collapsed", this);
38187                 this.fireEvent("invalidated", this);
38188             }
38189         }
38190         
38191     },
38192 */
38193     animateCollapse : function(){
38194         // overridden
38195     },
38196
38197     /**
38198      * Expands this region if it was previously collapsed.
38199      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38200      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38201      */
38202     /*
38203     expand : function(e, skipAnim){
38204         if(e) {
38205             e.stopPropagation();
38206         }
38207         if(!this.collapsed || this.el.hasActiveFx()) {
38208             return;
38209         }
38210         if(this.isSlid){
38211             this.afterSlideIn();
38212             skipAnim = true;
38213         }
38214         this.collapsed = false;
38215         if(this.config.animate && skipAnim !== true){
38216             this.animateExpand();
38217         }else{
38218             this.el.show();
38219             if(this.split){
38220                 this.split.el.show();
38221             }
38222             this.collapsedEl.setLocation(-2000,-2000);
38223             this.collapsedEl.hide();
38224             this.fireEvent("invalidated", this);
38225             this.fireEvent("expanded", this);
38226         }
38227     },
38228 */
38229     animateExpand : function(){
38230         // overridden
38231     },
38232
38233     initTabs : function()
38234     {
38235         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38236         
38237         var ts = new Roo.bootstrap.panel.Tabs({
38238             el: this.bodyEl.dom,
38239             region : this,
38240             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38241             disableTooltips: this.config.disableTabTips,
38242             toolbar : this.config.toolbar
38243         });
38244         
38245         if(this.config.hideTabs){
38246             ts.stripWrap.setDisplayed(false);
38247         }
38248         this.tabs = ts;
38249         ts.resizeTabs = this.config.resizeTabs === true;
38250         ts.minTabWidth = this.config.minTabWidth || 40;
38251         ts.maxTabWidth = this.config.maxTabWidth || 250;
38252         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38253         ts.monitorResize = false;
38254         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38255         ts.bodyEl.addClass('roo-layout-tabs-body');
38256         this.panels.each(this.initPanelAsTab, this);
38257     },
38258
38259     initPanelAsTab : function(panel){
38260         var ti = this.tabs.addTab(
38261             panel.getEl().id,
38262             panel.getTitle(),
38263             null,
38264             this.config.closeOnTab && panel.isClosable(),
38265             panel.tpl
38266         );
38267         if(panel.tabTip !== undefined){
38268             ti.setTooltip(panel.tabTip);
38269         }
38270         ti.on("activate", function(){
38271               this.setActivePanel(panel);
38272         }, this);
38273         
38274         if(this.config.closeOnTab){
38275             ti.on("beforeclose", function(t, e){
38276                 e.cancel = true;
38277                 this.remove(panel);
38278             }, this);
38279         }
38280         
38281         panel.tabItem = ti;
38282         
38283         return ti;
38284     },
38285
38286     updatePanelTitle : function(panel, title)
38287     {
38288         if(this.activePanel == panel){
38289             this.updateTitle(title);
38290         }
38291         if(this.tabs){
38292             var ti = this.tabs.getTab(panel.getEl().id);
38293             ti.setText(title);
38294             if(panel.tabTip !== undefined){
38295                 ti.setTooltip(panel.tabTip);
38296             }
38297         }
38298     },
38299
38300     updateTitle : function(title){
38301         if(this.titleTextEl && !this.config.title){
38302             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38303         }
38304     },
38305
38306     setActivePanel : function(panel)
38307     {
38308         panel = this.getPanel(panel);
38309         if(this.activePanel && this.activePanel != panel){
38310             if(this.activePanel.setActiveState(false) === false){
38311                 return;
38312             }
38313         }
38314         this.activePanel = panel;
38315         panel.setActiveState(true);
38316         if(this.panelSize){
38317             panel.setSize(this.panelSize.width, this.panelSize.height);
38318         }
38319         if(this.closeBtn){
38320             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38321         }
38322         this.updateTitle(panel.getTitle());
38323         if(this.tabs){
38324             this.fireEvent("invalidated", this);
38325         }
38326         this.fireEvent("panelactivated", this, panel);
38327     },
38328
38329     /**
38330      * Shows the specified panel.
38331      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38332      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38333      */
38334     showPanel : function(panel)
38335     {
38336         panel = this.getPanel(panel);
38337         if(panel){
38338             if(this.tabs){
38339                 var tab = this.tabs.getTab(panel.getEl().id);
38340                 if(tab.isHidden()){
38341                     this.tabs.unhideTab(tab.id);
38342                 }
38343                 tab.activate();
38344             }else{
38345                 this.setActivePanel(panel);
38346             }
38347         }
38348         return panel;
38349     },
38350
38351     /**
38352      * Get the active panel for this region.
38353      * @return {Roo.ContentPanel} The active panel or null
38354      */
38355     getActivePanel : function(){
38356         return this.activePanel;
38357     },
38358
38359     validateVisibility : function(){
38360         if(this.panels.getCount() < 1){
38361             this.updateTitle("&#160;");
38362             this.closeBtn.hide();
38363             this.hide();
38364         }else{
38365             if(!this.isVisible()){
38366                 this.show();
38367             }
38368         }
38369     },
38370
38371     /**
38372      * Adds the passed ContentPanel(s) to this region.
38373      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38374      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38375      */
38376     add : function(panel)
38377     {
38378         if(arguments.length > 1){
38379             for(var i = 0, len = arguments.length; i < len; i++) {
38380                 this.add(arguments[i]);
38381             }
38382             return null;
38383         }
38384         
38385         // if we have not been rendered yet, then we can not really do much of this..
38386         if (!this.bodyEl) {
38387             this.unrendered_panels.push(panel);
38388             return panel;
38389         }
38390         
38391         
38392         
38393         
38394         if(this.hasPanel(panel)){
38395             this.showPanel(panel);
38396             return panel;
38397         }
38398         panel.setRegion(this);
38399         this.panels.add(panel);
38400        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38401             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38402             // and hide them... ???
38403             this.bodyEl.dom.appendChild(panel.getEl().dom);
38404             if(panel.background !== true){
38405                 this.setActivePanel(panel);
38406             }
38407             this.fireEvent("paneladded", this, panel);
38408             return panel;
38409         }
38410         */
38411         if(!this.tabs){
38412             this.initTabs();
38413         }else{
38414             this.initPanelAsTab(panel);
38415         }
38416         
38417         
38418         if(panel.background !== true){
38419             this.tabs.activate(panel.getEl().id);
38420         }
38421         this.fireEvent("paneladded", this, panel);
38422         return panel;
38423     },
38424
38425     /**
38426      * Hides the tab for the specified panel.
38427      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38428      */
38429     hidePanel : function(panel){
38430         if(this.tabs && (panel = this.getPanel(panel))){
38431             this.tabs.hideTab(panel.getEl().id);
38432         }
38433     },
38434
38435     /**
38436      * Unhides the tab for a previously hidden panel.
38437      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38438      */
38439     unhidePanel : function(panel){
38440         if(this.tabs && (panel = this.getPanel(panel))){
38441             this.tabs.unhideTab(panel.getEl().id);
38442         }
38443     },
38444
38445     clearPanels : function(){
38446         while(this.panels.getCount() > 0){
38447              this.remove(this.panels.first());
38448         }
38449     },
38450
38451     /**
38452      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38453      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38454      * @param {Boolean} preservePanel Overrides the config preservePanel option
38455      * @return {Roo.ContentPanel} The panel that was removed
38456      */
38457     remove : function(panel, preservePanel)
38458     {
38459         panel = this.getPanel(panel);
38460         if(!panel){
38461             return null;
38462         }
38463         var e = {};
38464         this.fireEvent("beforeremove", this, panel, e);
38465         if(e.cancel === true){
38466             return null;
38467         }
38468         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38469         var panelId = panel.getId();
38470         this.panels.removeKey(panelId);
38471         if(preservePanel){
38472             document.body.appendChild(panel.getEl().dom);
38473         }
38474         if(this.tabs){
38475             this.tabs.removeTab(panel.getEl().id);
38476         }else if (!preservePanel){
38477             this.bodyEl.dom.removeChild(panel.getEl().dom);
38478         }
38479         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38480             var p = this.panels.first();
38481             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38482             tempEl.appendChild(p.getEl().dom);
38483             this.bodyEl.update("");
38484             this.bodyEl.dom.appendChild(p.getEl().dom);
38485             tempEl = null;
38486             this.updateTitle(p.getTitle());
38487             this.tabs = null;
38488             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38489             this.setActivePanel(p);
38490         }
38491         panel.setRegion(null);
38492         if(this.activePanel == panel){
38493             this.activePanel = null;
38494         }
38495         if(this.config.autoDestroy !== false && preservePanel !== true){
38496             try{panel.destroy();}catch(e){}
38497         }
38498         this.fireEvent("panelremoved", this, panel);
38499         return panel;
38500     },
38501
38502     /**
38503      * Returns the TabPanel component used by this region
38504      * @return {Roo.TabPanel}
38505      */
38506     getTabs : function(){
38507         return this.tabs;
38508     },
38509
38510     createTool : function(parentEl, className){
38511         var btn = Roo.DomHelper.append(parentEl, {
38512             tag: "div",
38513             cls: "x-layout-tools-button",
38514             children: [ {
38515                 tag: "div",
38516                 cls: "roo-layout-tools-button-inner " + className,
38517                 html: "&#160;"
38518             }]
38519         }, true);
38520         btn.addClassOnOver("roo-layout-tools-button-over");
38521         return btn;
38522     }
38523 });/*
38524  * Based on:
38525  * Ext JS Library 1.1.1
38526  * Copyright(c) 2006-2007, Ext JS, LLC.
38527  *
38528  * Originally Released Under LGPL - original licence link has changed is not relivant.
38529  *
38530  * Fork - LGPL
38531  * <script type="text/javascript">
38532  */
38533  
38534
38535
38536 /**
38537  * @class Roo.SplitLayoutRegion
38538  * @extends Roo.LayoutRegion
38539  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38540  */
38541 Roo.bootstrap.layout.Split = function(config){
38542     this.cursor = config.cursor;
38543     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38544 };
38545
38546 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38547 {
38548     splitTip : "Drag to resize.",
38549     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38550     useSplitTips : false,
38551
38552     applyConfig : function(config){
38553         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38554     },
38555     
38556     onRender : function(ctr,pos) {
38557         
38558         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38559         if(!this.config.split){
38560             return;
38561         }
38562         if(!this.split){
38563             
38564             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38565                             tag: "div",
38566                             id: this.el.id + "-split",
38567                             cls: "roo-layout-split roo-layout-split-"+this.position,
38568                             html: "&#160;"
38569             });
38570             /** The SplitBar for this region 
38571             * @type Roo.SplitBar */
38572             // does not exist yet...
38573             Roo.log([this.position, this.orientation]);
38574             
38575             this.split = new Roo.bootstrap.SplitBar({
38576                 dragElement : splitEl,
38577                 resizingElement: this.el,
38578                 orientation : this.orientation
38579             });
38580             
38581             this.split.on("moved", this.onSplitMove, this);
38582             this.split.useShim = this.config.useShim === true;
38583             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38584             if(this.useSplitTips){
38585                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38586             }
38587             //if(config.collapsible){
38588             //    this.split.el.on("dblclick", this.collapse,  this);
38589             //}
38590         }
38591         if(typeof this.config.minSize != "undefined"){
38592             this.split.minSize = this.config.minSize;
38593         }
38594         if(typeof this.config.maxSize != "undefined"){
38595             this.split.maxSize = this.config.maxSize;
38596         }
38597         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38598             this.hideSplitter();
38599         }
38600         
38601     },
38602
38603     getHMaxSize : function(){
38604          var cmax = this.config.maxSize || 10000;
38605          var center = this.mgr.getRegion("center");
38606          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38607     },
38608
38609     getVMaxSize : function(){
38610          var cmax = this.config.maxSize || 10000;
38611          var center = this.mgr.getRegion("center");
38612          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38613     },
38614
38615     onSplitMove : function(split, newSize){
38616         this.fireEvent("resized", this, newSize);
38617     },
38618     
38619     /** 
38620      * Returns the {@link Roo.SplitBar} for this region.
38621      * @return {Roo.SplitBar}
38622      */
38623     getSplitBar : function(){
38624         return this.split;
38625     },
38626     
38627     hide : function(){
38628         this.hideSplitter();
38629         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38630     },
38631
38632     hideSplitter : function(){
38633         if(this.split){
38634             this.split.el.setLocation(-2000,-2000);
38635             this.split.el.hide();
38636         }
38637     },
38638
38639     show : function(){
38640         if(this.split){
38641             this.split.el.show();
38642         }
38643         Roo.bootstrap.layout.Split.superclass.show.call(this);
38644     },
38645     
38646     beforeSlide: function(){
38647         if(Roo.isGecko){// firefox overflow auto bug workaround
38648             this.bodyEl.clip();
38649             if(this.tabs) {
38650                 this.tabs.bodyEl.clip();
38651             }
38652             if(this.activePanel){
38653                 this.activePanel.getEl().clip();
38654                 
38655                 if(this.activePanel.beforeSlide){
38656                     this.activePanel.beforeSlide();
38657                 }
38658             }
38659         }
38660     },
38661     
38662     afterSlide : function(){
38663         if(Roo.isGecko){// firefox overflow auto bug workaround
38664             this.bodyEl.unclip();
38665             if(this.tabs) {
38666                 this.tabs.bodyEl.unclip();
38667             }
38668             if(this.activePanel){
38669                 this.activePanel.getEl().unclip();
38670                 if(this.activePanel.afterSlide){
38671                     this.activePanel.afterSlide();
38672                 }
38673             }
38674         }
38675     },
38676
38677     initAutoHide : function(){
38678         if(this.autoHide !== false){
38679             if(!this.autoHideHd){
38680                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38681                 this.autoHideHd = {
38682                     "mouseout": function(e){
38683                         if(!e.within(this.el, true)){
38684                             st.delay(500);
38685                         }
38686                     },
38687                     "mouseover" : function(e){
38688                         st.cancel();
38689                     },
38690                     scope : this
38691                 };
38692             }
38693             this.el.on(this.autoHideHd);
38694         }
38695     },
38696
38697     clearAutoHide : function(){
38698         if(this.autoHide !== false){
38699             this.el.un("mouseout", this.autoHideHd.mouseout);
38700             this.el.un("mouseover", this.autoHideHd.mouseover);
38701         }
38702     },
38703
38704     clearMonitor : function(){
38705         Roo.get(document).un("click", this.slideInIf, this);
38706     },
38707
38708     // these names are backwards but not changed for compat
38709     slideOut : function(){
38710         if(this.isSlid || this.el.hasActiveFx()){
38711             return;
38712         }
38713         this.isSlid = true;
38714         if(this.collapseBtn){
38715             this.collapseBtn.hide();
38716         }
38717         this.closeBtnState = this.closeBtn.getStyle('display');
38718         this.closeBtn.hide();
38719         if(this.stickBtn){
38720             this.stickBtn.show();
38721         }
38722         this.el.show();
38723         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38724         this.beforeSlide();
38725         this.el.setStyle("z-index", 10001);
38726         this.el.slideIn(this.getSlideAnchor(), {
38727             callback: function(){
38728                 this.afterSlide();
38729                 this.initAutoHide();
38730                 Roo.get(document).on("click", this.slideInIf, this);
38731                 this.fireEvent("slideshow", this);
38732             },
38733             scope: this,
38734             block: true
38735         });
38736     },
38737
38738     afterSlideIn : function(){
38739         this.clearAutoHide();
38740         this.isSlid = false;
38741         this.clearMonitor();
38742         this.el.setStyle("z-index", "");
38743         if(this.collapseBtn){
38744             this.collapseBtn.show();
38745         }
38746         this.closeBtn.setStyle('display', this.closeBtnState);
38747         if(this.stickBtn){
38748             this.stickBtn.hide();
38749         }
38750         this.fireEvent("slidehide", this);
38751     },
38752
38753     slideIn : function(cb){
38754         if(!this.isSlid || this.el.hasActiveFx()){
38755             Roo.callback(cb);
38756             return;
38757         }
38758         this.isSlid = false;
38759         this.beforeSlide();
38760         this.el.slideOut(this.getSlideAnchor(), {
38761             callback: function(){
38762                 this.el.setLeftTop(-10000, -10000);
38763                 this.afterSlide();
38764                 this.afterSlideIn();
38765                 Roo.callback(cb);
38766             },
38767             scope: this,
38768             block: true
38769         });
38770     },
38771     
38772     slideInIf : function(e){
38773         if(!e.within(this.el)){
38774             this.slideIn();
38775         }
38776     },
38777
38778     animateCollapse : function(){
38779         this.beforeSlide();
38780         this.el.setStyle("z-index", 20000);
38781         var anchor = this.getSlideAnchor();
38782         this.el.slideOut(anchor, {
38783             callback : function(){
38784                 this.el.setStyle("z-index", "");
38785                 this.collapsedEl.slideIn(anchor, {duration:.3});
38786                 this.afterSlide();
38787                 this.el.setLocation(-10000,-10000);
38788                 this.el.hide();
38789                 this.fireEvent("collapsed", this);
38790             },
38791             scope: this,
38792             block: true
38793         });
38794     },
38795
38796     animateExpand : function(){
38797         this.beforeSlide();
38798         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38799         this.el.setStyle("z-index", 20000);
38800         this.collapsedEl.hide({
38801             duration:.1
38802         });
38803         this.el.slideIn(this.getSlideAnchor(), {
38804             callback : function(){
38805                 this.el.setStyle("z-index", "");
38806                 this.afterSlide();
38807                 if(this.split){
38808                     this.split.el.show();
38809                 }
38810                 this.fireEvent("invalidated", this);
38811                 this.fireEvent("expanded", this);
38812             },
38813             scope: this,
38814             block: true
38815         });
38816     },
38817
38818     anchors : {
38819         "west" : "left",
38820         "east" : "right",
38821         "north" : "top",
38822         "south" : "bottom"
38823     },
38824
38825     sanchors : {
38826         "west" : "l",
38827         "east" : "r",
38828         "north" : "t",
38829         "south" : "b"
38830     },
38831
38832     canchors : {
38833         "west" : "tl-tr",
38834         "east" : "tr-tl",
38835         "north" : "tl-bl",
38836         "south" : "bl-tl"
38837     },
38838
38839     getAnchor : function(){
38840         return this.anchors[this.position];
38841     },
38842
38843     getCollapseAnchor : function(){
38844         return this.canchors[this.position];
38845     },
38846
38847     getSlideAnchor : function(){
38848         return this.sanchors[this.position];
38849     },
38850
38851     getAlignAdj : function(){
38852         var cm = this.cmargins;
38853         switch(this.position){
38854             case "west":
38855                 return [0, 0];
38856             break;
38857             case "east":
38858                 return [0, 0];
38859             break;
38860             case "north":
38861                 return [0, 0];
38862             break;
38863             case "south":
38864                 return [0, 0];
38865             break;
38866         }
38867     },
38868
38869     getExpandAdj : function(){
38870         var c = this.collapsedEl, cm = this.cmargins;
38871         switch(this.position){
38872             case "west":
38873                 return [-(cm.right+c.getWidth()+cm.left), 0];
38874             break;
38875             case "east":
38876                 return [cm.right+c.getWidth()+cm.left, 0];
38877             break;
38878             case "north":
38879                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38880             break;
38881             case "south":
38882                 return [0, cm.top+cm.bottom+c.getHeight()];
38883             break;
38884         }
38885     }
38886 });/*
38887  * Based on:
38888  * Ext JS Library 1.1.1
38889  * Copyright(c) 2006-2007, Ext JS, LLC.
38890  *
38891  * Originally Released Under LGPL - original licence link has changed is not relivant.
38892  *
38893  * Fork - LGPL
38894  * <script type="text/javascript">
38895  */
38896 /*
38897  * These classes are private internal classes
38898  */
38899 Roo.bootstrap.layout.Center = function(config){
38900     config.region = "center";
38901     Roo.bootstrap.layout.Region.call(this, config);
38902     this.visible = true;
38903     this.minWidth = config.minWidth || 20;
38904     this.minHeight = config.minHeight || 20;
38905 };
38906
38907 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38908     hide : function(){
38909         // center panel can't be hidden
38910     },
38911     
38912     show : function(){
38913         // center panel can't be hidden
38914     },
38915     
38916     getMinWidth: function(){
38917         return this.minWidth;
38918     },
38919     
38920     getMinHeight: function(){
38921         return this.minHeight;
38922     }
38923 });
38924
38925
38926
38927
38928  
38929
38930
38931
38932
38933
38934
38935 Roo.bootstrap.layout.North = function(config)
38936 {
38937     config.region = 'north';
38938     config.cursor = 'n-resize';
38939     
38940     Roo.bootstrap.layout.Split.call(this, config);
38941     
38942     
38943     if(this.split){
38944         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38945         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38946         this.split.el.addClass("roo-layout-split-v");
38947     }
38948     var size = config.initialSize || config.height;
38949     if(typeof size != "undefined"){
38950         this.el.setHeight(size);
38951     }
38952 };
38953 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38954 {
38955     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38956     
38957     
38958     
38959     getBox : function(){
38960         if(this.collapsed){
38961             return this.collapsedEl.getBox();
38962         }
38963         var box = this.el.getBox();
38964         if(this.split){
38965             box.height += this.split.el.getHeight();
38966         }
38967         return box;
38968     },
38969     
38970     updateBox : function(box){
38971         if(this.split && !this.collapsed){
38972             box.height -= this.split.el.getHeight();
38973             this.split.el.setLeft(box.x);
38974             this.split.el.setTop(box.y+box.height);
38975             this.split.el.setWidth(box.width);
38976         }
38977         if(this.collapsed){
38978             this.updateBody(box.width, null);
38979         }
38980         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38981     }
38982 });
38983
38984
38985
38986
38987
38988 Roo.bootstrap.layout.South = function(config){
38989     config.region = 'south';
38990     config.cursor = 's-resize';
38991     Roo.bootstrap.layout.Split.call(this, config);
38992     if(this.split){
38993         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38994         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38995         this.split.el.addClass("roo-layout-split-v");
38996     }
38997     var size = config.initialSize || config.height;
38998     if(typeof size != "undefined"){
38999         this.el.setHeight(size);
39000     }
39001 };
39002
39003 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39004     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39005     getBox : function(){
39006         if(this.collapsed){
39007             return this.collapsedEl.getBox();
39008         }
39009         var box = this.el.getBox();
39010         if(this.split){
39011             var sh = this.split.el.getHeight();
39012             box.height += sh;
39013             box.y -= sh;
39014         }
39015         return box;
39016     },
39017     
39018     updateBox : function(box){
39019         if(this.split && !this.collapsed){
39020             var sh = this.split.el.getHeight();
39021             box.height -= sh;
39022             box.y += sh;
39023             this.split.el.setLeft(box.x);
39024             this.split.el.setTop(box.y-sh);
39025             this.split.el.setWidth(box.width);
39026         }
39027         if(this.collapsed){
39028             this.updateBody(box.width, null);
39029         }
39030         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39031     }
39032 });
39033
39034 Roo.bootstrap.layout.East = function(config){
39035     config.region = "east";
39036     config.cursor = "e-resize";
39037     Roo.bootstrap.layout.Split.call(this, config);
39038     if(this.split){
39039         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39040         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39041         this.split.el.addClass("roo-layout-split-h");
39042     }
39043     var size = config.initialSize || config.width;
39044     if(typeof size != "undefined"){
39045         this.el.setWidth(size);
39046     }
39047 };
39048 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39049     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39050     getBox : function(){
39051         if(this.collapsed){
39052             return this.collapsedEl.getBox();
39053         }
39054         var box = this.el.getBox();
39055         if(this.split){
39056             var sw = this.split.el.getWidth();
39057             box.width += sw;
39058             box.x -= sw;
39059         }
39060         return box;
39061     },
39062
39063     updateBox : function(box){
39064         if(this.split && !this.collapsed){
39065             var sw = this.split.el.getWidth();
39066             box.width -= sw;
39067             this.split.el.setLeft(box.x);
39068             this.split.el.setTop(box.y);
39069             this.split.el.setHeight(box.height);
39070             box.x += sw;
39071         }
39072         if(this.collapsed){
39073             this.updateBody(null, box.height);
39074         }
39075         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39076     }
39077 });
39078
39079 Roo.bootstrap.layout.West = function(config){
39080     config.region = "west";
39081     config.cursor = "w-resize";
39082     
39083     Roo.bootstrap.layout.Split.call(this, config);
39084     if(this.split){
39085         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39086         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39087         this.split.el.addClass("roo-layout-split-h");
39088     }
39089     
39090 };
39091 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39092     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39093     
39094     onRender: function(ctr, pos)
39095     {
39096         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39097         var size = this.config.initialSize || this.config.width;
39098         if(typeof size != "undefined"){
39099             this.el.setWidth(size);
39100         }
39101     },
39102     
39103     getBox : function(){
39104         if(this.collapsed){
39105             return this.collapsedEl.getBox();
39106         }
39107         var box = this.el.getBox();
39108         if(this.split){
39109             box.width += this.split.el.getWidth();
39110         }
39111         return box;
39112     },
39113     
39114     updateBox : function(box){
39115         if(this.split && !this.collapsed){
39116             var sw = this.split.el.getWidth();
39117             box.width -= sw;
39118             this.split.el.setLeft(box.x+box.width);
39119             this.split.el.setTop(box.y);
39120             this.split.el.setHeight(box.height);
39121         }
39122         if(this.collapsed){
39123             this.updateBody(null, box.height);
39124         }
39125         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39126     }
39127 });Roo.namespace("Roo.bootstrap.panel");/*
39128  * Based on:
39129  * Ext JS Library 1.1.1
39130  * Copyright(c) 2006-2007, Ext JS, LLC.
39131  *
39132  * Originally Released Under LGPL - original licence link has changed is not relivant.
39133  *
39134  * Fork - LGPL
39135  * <script type="text/javascript">
39136  */
39137 /**
39138  * @class Roo.ContentPanel
39139  * @extends Roo.util.Observable
39140  * A basic ContentPanel element.
39141  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39142  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39143  * @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
39144  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39145  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39146  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39147  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39148  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39149  * @cfg {String} title          The title for this panel
39150  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39151  * @cfg {String} url            Calls {@link #setUrl} with this value
39152  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39153  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39154  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39155  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39156  * @cfg {Boolean} badges render the badges
39157  * @cfg {String} cls  extra classes to use  
39158  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39159
39160  * @constructor
39161  * Create a new ContentPanel.
39162  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39163  * @param {String/Object} config A string to set only the title or a config object
39164  * @param {String} content (optional) Set the HTML content for this panel
39165  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39166  */
39167 Roo.bootstrap.panel.Content = function( config){
39168     
39169     this.tpl = config.tpl || false;
39170     
39171     var el = config.el;
39172     var content = config.content;
39173
39174     if(config.autoCreate){ // xtype is available if this is called from factory
39175         el = Roo.id();
39176     }
39177     this.el = Roo.get(el);
39178     if(!this.el && config && config.autoCreate){
39179         if(typeof config.autoCreate == "object"){
39180             if(!config.autoCreate.id){
39181                 config.autoCreate.id = config.id||el;
39182             }
39183             this.el = Roo.DomHelper.append(document.body,
39184                         config.autoCreate, true);
39185         }else{
39186             var elcfg =  {
39187                 tag: "div",
39188                 cls: (config.cls || '') +
39189                     (config.background ? ' bg-' + config.background : '') +
39190                     " roo-layout-inactive-content",
39191                 id: config.id||el
39192             };
39193             if (config.html) {
39194                 elcfg.html = config.html;
39195                 
39196             }
39197                         
39198             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39199         }
39200     } 
39201     this.closable = false;
39202     this.loaded = false;
39203     this.active = false;
39204    
39205       
39206     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39207         
39208         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39209         
39210         this.wrapEl = this.el; //this.el.wrap();
39211         var ti = [];
39212         if (config.toolbar.items) {
39213             ti = config.toolbar.items ;
39214             delete config.toolbar.items ;
39215         }
39216         
39217         var nitems = [];
39218         this.toolbar.render(this.wrapEl, 'before');
39219         for(var i =0;i < ti.length;i++) {
39220           //  Roo.log(['add child', items[i]]);
39221             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39222         }
39223         this.toolbar.items = nitems;
39224         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39225         delete config.toolbar;
39226         
39227     }
39228     /*
39229     // xtype created footer. - not sure if will work as we normally have to render first..
39230     if (this.footer && !this.footer.el && this.footer.xtype) {
39231         if (!this.wrapEl) {
39232             this.wrapEl = this.el.wrap();
39233         }
39234     
39235         this.footer.container = this.wrapEl.createChild();
39236          
39237         this.footer = Roo.factory(this.footer, Roo);
39238         
39239     }
39240     */
39241     
39242      if(typeof config == "string"){
39243         this.title = config;
39244     }else{
39245         Roo.apply(this, config);
39246     }
39247     
39248     if(this.resizeEl){
39249         this.resizeEl = Roo.get(this.resizeEl, true);
39250     }else{
39251         this.resizeEl = this.el;
39252     }
39253     // handle view.xtype
39254     
39255  
39256     
39257     
39258     this.addEvents({
39259         /**
39260          * @event activate
39261          * Fires when this panel is activated. 
39262          * @param {Roo.ContentPanel} this
39263          */
39264         "activate" : true,
39265         /**
39266          * @event deactivate
39267          * Fires when this panel is activated. 
39268          * @param {Roo.ContentPanel} this
39269          */
39270         "deactivate" : true,
39271
39272         /**
39273          * @event resize
39274          * Fires when this panel is resized if fitToFrame is true.
39275          * @param {Roo.ContentPanel} this
39276          * @param {Number} width The width after any component adjustments
39277          * @param {Number} height The height after any component adjustments
39278          */
39279         "resize" : true,
39280         
39281          /**
39282          * @event render
39283          * Fires when this tab is created
39284          * @param {Roo.ContentPanel} this
39285          */
39286         "render" : true
39287         
39288         
39289         
39290     });
39291     
39292
39293     
39294     
39295     if(this.autoScroll){
39296         this.resizeEl.setStyle("overflow", "auto");
39297     } else {
39298         // fix randome scrolling
39299         //this.el.on('scroll', function() {
39300         //    Roo.log('fix random scolling');
39301         //    this.scrollTo('top',0); 
39302         //});
39303     }
39304     content = content || this.content;
39305     if(content){
39306         this.setContent(content);
39307     }
39308     if(config && config.url){
39309         this.setUrl(this.url, this.params, this.loadOnce);
39310     }
39311     
39312     
39313     
39314     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39315     
39316     if (this.view && typeof(this.view.xtype) != 'undefined') {
39317         this.view.el = this.el.appendChild(document.createElement("div"));
39318         this.view = Roo.factory(this.view); 
39319         this.view.render  &&  this.view.render(false, '');  
39320     }
39321     
39322     
39323     this.fireEvent('render', this);
39324 };
39325
39326 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39327     
39328     cls : '',
39329     background : '',
39330     
39331     tabTip : '',
39332     
39333     setRegion : function(region){
39334         this.region = region;
39335         this.setActiveClass(region && !this.background);
39336     },
39337     
39338     
39339     setActiveClass: function(state)
39340     {
39341         if(state){
39342            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39343            this.el.setStyle('position','relative');
39344         }else{
39345            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39346            this.el.setStyle('position', 'absolute');
39347         } 
39348     },
39349     
39350     /**
39351      * Returns the toolbar for this Panel if one was configured. 
39352      * @return {Roo.Toolbar} 
39353      */
39354     getToolbar : function(){
39355         return this.toolbar;
39356     },
39357     
39358     setActiveState : function(active)
39359     {
39360         this.active = active;
39361         this.setActiveClass(active);
39362         if(!active){
39363             if(this.fireEvent("deactivate", this) === false){
39364                 return false;
39365             }
39366             return true;
39367         }
39368         this.fireEvent("activate", this);
39369         return true;
39370     },
39371     /**
39372      * Updates this panel's element
39373      * @param {String} content The new content
39374      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39375     */
39376     setContent : function(content, loadScripts){
39377         this.el.update(content, loadScripts);
39378     },
39379
39380     ignoreResize : function(w, h){
39381         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39382             return true;
39383         }else{
39384             this.lastSize = {width: w, height: h};
39385             return false;
39386         }
39387     },
39388     /**
39389      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39390      * @return {Roo.UpdateManager} The UpdateManager
39391      */
39392     getUpdateManager : function(){
39393         return this.el.getUpdateManager();
39394     },
39395      /**
39396      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39397      * @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:
39398 <pre><code>
39399 panel.load({
39400     url: "your-url.php",
39401     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39402     callback: yourFunction,
39403     scope: yourObject, //(optional scope)
39404     discardUrl: false,
39405     nocache: false,
39406     text: "Loading...",
39407     timeout: 30,
39408     scripts: false
39409 });
39410 </code></pre>
39411      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39412      * 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.
39413      * @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}
39414      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39415      * @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.
39416      * @return {Roo.ContentPanel} this
39417      */
39418     load : function(){
39419         var um = this.el.getUpdateManager();
39420         um.update.apply(um, arguments);
39421         return this;
39422     },
39423
39424
39425     /**
39426      * 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.
39427      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39428      * @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)
39429      * @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)
39430      * @return {Roo.UpdateManager} The UpdateManager
39431      */
39432     setUrl : function(url, params, loadOnce){
39433         if(this.refreshDelegate){
39434             this.removeListener("activate", this.refreshDelegate);
39435         }
39436         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39437         this.on("activate", this.refreshDelegate);
39438         return this.el.getUpdateManager();
39439     },
39440     
39441     _handleRefresh : function(url, params, loadOnce){
39442         if(!loadOnce || !this.loaded){
39443             var updater = this.el.getUpdateManager();
39444             updater.update(url, params, this._setLoaded.createDelegate(this));
39445         }
39446     },
39447     
39448     _setLoaded : function(){
39449         this.loaded = true;
39450     }, 
39451     
39452     /**
39453      * Returns this panel's id
39454      * @return {String} 
39455      */
39456     getId : function(){
39457         return this.el.id;
39458     },
39459     
39460     /** 
39461      * Returns this panel's element - used by regiosn to add.
39462      * @return {Roo.Element} 
39463      */
39464     getEl : function(){
39465         return this.wrapEl || this.el;
39466     },
39467     
39468    
39469     
39470     adjustForComponents : function(width, height)
39471     {
39472         //Roo.log('adjustForComponents ');
39473         if(this.resizeEl != this.el){
39474             width -= this.el.getFrameWidth('lr');
39475             height -= this.el.getFrameWidth('tb');
39476         }
39477         if(this.toolbar){
39478             var te = this.toolbar.getEl();
39479             te.setWidth(width);
39480             height -= te.getHeight();
39481         }
39482         if(this.footer){
39483             var te = this.footer.getEl();
39484             te.setWidth(width);
39485             height -= te.getHeight();
39486         }
39487         
39488         
39489         if(this.adjustments){
39490             width += this.adjustments[0];
39491             height += this.adjustments[1];
39492         }
39493         return {"width": width, "height": height};
39494     },
39495     
39496     setSize : function(width, height){
39497         if(this.fitToFrame && !this.ignoreResize(width, height)){
39498             if(this.fitContainer && this.resizeEl != this.el){
39499                 this.el.setSize(width, height);
39500             }
39501             var size = this.adjustForComponents(width, height);
39502             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39503             this.fireEvent('resize', this, size.width, size.height);
39504         }
39505     },
39506     
39507     /**
39508      * Returns this panel's title
39509      * @return {String} 
39510      */
39511     getTitle : function(){
39512         
39513         if (typeof(this.title) != 'object') {
39514             return this.title;
39515         }
39516         
39517         var t = '';
39518         for (var k in this.title) {
39519             if (!this.title.hasOwnProperty(k)) {
39520                 continue;
39521             }
39522             
39523             if (k.indexOf('-') >= 0) {
39524                 var s = k.split('-');
39525                 for (var i = 0; i<s.length; i++) {
39526                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39527                 }
39528             } else {
39529                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39530             }
39531         }
39532         return t;
39533     },
39534     
39535     /**
39536      * Set this panel's title
39537      * @param {String} title
39538      */
39539     setTitle : function(title){
39540         this.title = title;
39541         if(this.region){
39542             this.region.updatePanelTitle(this, title);
39543         }
39544     },
39545     
39546     /**
39547      * Returns true is this panel was configured to be closable
39548      * @return {Boolean} 
39549      */
39550     isClosable : function(){
39551         return this.closable;
39552     },
39553     
39554     beforeSlide : function(){
39555         this.el.clip();
39556         this.resizeEl.clip();
39557     },
39558     
39559     afterSlide : function(){
39560         this.el.unclip();
39561         this.resizeEl.unclip();
39562     },
39563     
39564     /**
39565      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39566      *   Will fail silently if the {@link #setUrl} method has not been called.
39567      *   This does not activate the panel, just updates its content.
39568      */
39569     refresh : function(){
39570         if(this.refreshDelegate){
39571            this.loaded = false;
39572            this.refreshDelegate();
39573         }
39574     },
39575     
39576     /**
39577      * Destroys this panel
39578      */
39579     destroy : function(){
39580         this.el.removeAllListeners();
39581         var tempEl = document.createElement("span");
39582         tempEl.appendChild(this.el.dom);
39583         tempEl.innerHTML = "";
39584         this.el.remove();
39585         this.el = null;
39586     },
39587     
39588     /**
39589      * form - if the content panel contains a form - this is a reference to it.
39590      * @type {Roo.form.Form}
39591      */
39592     form : false,
39593     /**
39594      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39595      *    This contains a reference to it.
39596      * @type {Roo.View}
39597      */
39598     view : false,
39599     
39600       /**
39601      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39602      * <pre><code>
39603
39604 layout.addxtype({
39605        xtype : 'Form',
39606        items: [ .... ]
39607    }
39608 );
39609
39610 </code></pre>
39611      * @param {Object} cfg Xtype definition of item to add.
39612      */
39613     
39614     
39615     getChildContainer: function () {
39616         return this.getEl();
39617     }
39618     
39619     
39620     /*
39621         var  ret = new Roo.factory(cfg);
39622         return ret;
39623         
39624         
39625         // add form..
39626         if (cfg.xtype.match(/^Form$/)) {
39627             
39628             var el;
39629             //if (this.footer) {
39630             //    el = this.footer.container.insertSibling(false, 'before');
39631             //} else {
39632                 el = this.el.createChild();
39633             //}
39634
39635             this.form = new  Roo.form.Form(cfg);
39636             
39637             
39638             if ( this.form.allItems.length) {
39639                 this.form.render(el.dom);
39640             }
39641             return this.form;
39642         }
39643         // should only have one of theses..
39644         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39645             // views.. should not be just added - used named prop 'view''
39646             
39647             cfg.el = this.el.appendChild(document.createElement("div"));
39648             // factory?
39649             
39650             var ret = new Roo.factory(cfg);
39651              
39652              ret.render && ret.render(false, ''); // render blank..
39653             this.view = ret;
39654             return ret;
39655         }
39656         return false;
39657     }
39658     \*/
39659 });
39660  
39661 /**
39662  * @class Roo.bootstrap.panel.Grid
39663  * @extends Roo.bootstrap.panel.Content
39664  * @constructor
39665  * Create a new GridPanel.
39666  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39667  * @param {Object} config A the config object
39668   
39669  */
39670
39671
39672
39673 Roo.bootstrap.panel.Grid = function(config)
39674 {
39675     
39676       
39677     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39678         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39679
39680     config.el = this.wrapper;
39681     //this.el = this.wrapper;
39682     
39683       if (config.container) {
39684         // ctor'ed from a Border/panel.grid
39685         
39686         
39687         this.wrapper.setStyle("overflow", "hidden");
39688         this.wrapper.addClass('roo-grid-container');
39689
39690     }
39691     
39692     
39693     if(config.toolbar){
39694         var tool_el = this.wrapper.createChild();    
39695         this.toolbar = Roo.factory(config.toolbar);
39696         var ti = [];
39697         if (config.toolbar.items) {
39698             ti = config.toolbar.items ;
39699             delete config.toolbar.items ;
39700         }
39701         
39702         var nitems = [];
39703         this.toolbar.render(tool_el);
39704         for(var i =0;i < ti.length;i++) {
39705           //  Roo.log(['add child', items[i]]);
39706             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39707         }
39708         this.toolbar.items = nitems;
39709         
39710         delete config.toolbar;
39711     }
39712     
39713     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39714     config.grid.scrollBody = true;;
39715     config.grid.monitorWindowResize = false; // turn off autosizing
39716     config.grid.autoHeight = false;
39717     config.grid.autoWidth = false;
39718     
39719     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39720     
39721     if (config.background) {
39722         // render grid on panel activation (if panel background)
39723         this.on('activate', function(gp) {
39724             if (!gp.grid.rendered) {
39725                 gp.grid.render(this.wrapper);
39726                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39727             }
39728         });
39729             
39730     } else {
39731         this.grid.render(this.wrapper);
39732         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39733
39734     }
39735     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39736     // ??? needed ??? config.el = this.wrapper;
39737     
39738     
39739     
39740   
39741     // xtype created footer. - not sure if will work as we normally have to render first..
39742     if (this.footer && !this.footer.el && this.footer.xtype) {
39743         
39744         var ctr = this.grid.getView().getFooterPanel(true);
39745         this.footer.dataSource = this.grid.dataSource;
39746         this.footer = Roo.factory(this.footer, Roo);
39747         this.footer.render(ctr);
39748         
39749     }
39750     
39751     
39752     
39753     
39754      
39755 };
39756
39757 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39758     getId : function(){
39759         return this.grid.id;
39760     },
39761     
39762     /**
39763      * Returns the grid for this panel
39764      * @return {Roo.bootstrap.Table} 
39765      */
39766     getGrid : function(){
39767         return this.grid;    
39768     },
39769     
39770     setSize : function(width, height){
39771         if(!this.ignoreResize(width, height)){
39772             var grid = this.grid;
39773             var size = this.adjustForComponents(width, height);
39774             // tfoot is not a footer?
39775           
39776             
39777             var gridel = grid.getGridEl();
39778             gridel.setSize(size.width, size.height);
39779             
39780             var tbd = grid.getGridEl().select('tbody', true).first();
39781             var thd = grid.getGridEl().select('thead',true).first();
39782             var tbf= grid.getGridEl().select('tfoot', true).first();
39783
39784             if (tbf) {
39785                 size.height -= thd.getHeight();
39786             }
39787             if (thd) {
39788                 size.height -= thd.getHeight();
39789             }
39790             
39791             tbd.setSize(size.width, size.height );
39792             // this is for the account management tab -seems to work there.
39793             var thd = grid.getGridEl().select('thead',true).first();
39794             //if (tbd) {
39795             //    tbd.setSize(size.width, size.height - thd.getHeight());
39796             //}
39797              
39798             grid.autoSize();
39799         }
39800     },
39801      
39802     
39803     
39804     beforeSlide : function(){
39805         this.grid.getView().scroller.clip();
39806     },
39807     
39808     afterSlide : function(){
39809         this.grid.getView().scroller.unclip();
39810     },
39811     
39812     destroy : function(){
39813         this.grid.destroy();
39814         delete this.grid;
39815         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39816     }
39817 });
39818
39819 /**
39820  * @class Roo.bootstrap.panel.Nest
39821  * @extends Roo.bootstrap.panel.Content
39822  * @constructor
39823  * Create a new Panel, that can contain a layout.Border.
39824  * 
39825  * 
39826  * @param {Roo.BorderLayout} layout The layout for this panel
39827  * @param {String/Object} config A string to set only the title or a config object
39828  */
39829 Roo.bootstrap.panel.Nest = function(config)
39830 {
39831     // construct with only one argument..
39832     /* FIXME - implement nicer consturctors
39833     if (layout.layout) {
39834         config = layout;
39835         layout = config.layout;
39836         delete config.layout;
39837     }
39838     if (layout.xtype && !layout.getEl) {
39839         // then layout needs constructing..
39840         layout = Roo.factory(layout, Roo);
39841     }
39842     */
39843     
39844     config.el =  config.layout.getEl();
39845     
39846     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39847     
39848     config.layout.monitorWindowResize = false; // turn off autosizing
39849     this.layout = config.layout;
39850     this.layout.getEl().addClass("roo-layout-nested-layout");
39851     this.layout.parent = this;
39852     
39853     
39854     
39855     
39856 };
39857
39858 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39859
39860     setSize : function(width, height){
39861         if(!this.ignoreResize(width, height)){
39862             var size = this.adjustForComponents(width, height);
39863             var el = this.layout.getEl();
39864             if (size.height < 1) {
39865                 el.setWidth(size.width);   
39866             } else {
39867                 el.setSize(size.width, size.height);
39868             }
39869             var touch = el.dom.offsetWidth;
39870             this.layout.layout();
39871             // ie requires a double layout on the first pass
39872             if(Roo.isIE && !this.initialized){
39873                 this.initialized = true;
39874                 this.layout.layout();
39875             }
39876         }
39877     },
39878     
39879     // activate all subpanels if not currently active..
39880     
39881     setActiveState : function(active){
39882         this.active = active;
39883         this.setActiveClass(active);
39884         
39885         if(!active){
39886             this.fireEvent("deactivate", this);
39887             return;
39888         }
39889         
39890         this.fireEvent("activate", this);
39891         // not sure if this should happen before or after..
39892         if (!this.layout) {
39893             return; // should not happen..
39894         }
39895         var reg = false;
39896         for (var r in this.layout.regions) {
39897             reg = this.layout.getRegion(r);
39898             if (reg.getActivePanel()) {
39899                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39900                 reg.setActivePanel(reg.getActivePanel());
39901                 continue;
39902             }
39903             if (!reg.panels.length) {
39904                 continue;
39905             }
39906             reg.showPanel(reg.getPanel(0));
39907         }
39908         
39909         
39910         
39911         
39912     },
39913     
39914     /**
39915      * Returns the nested BorderLayout for this panel
39916      * @return {Roo.BorderLayout} 
39917      */
39918     getLayout : function(){
39919         return this.layout;
39920     },
39921     
39922      /**
39923      * Adds a xtype elements to the layout of the nested panel
39924      * <pre><code>
39925
39926 panel.addxtype({
39927        xtype : 'ContentPanel',
39928        region: 'west',
39929        items: [ .... ]
39930    }
39931 );
39932
39933 panel.addxtype({
39934         xtype : 'NestedLayoutPanel',
39935         region: 'west',
39936         layout: {
39937            center: { },
39938            west: { }   
39939         },
39940         items : [ ... list of content panels or nested layout panels.. ]
39941    }
39942 );
39943 </code></pre>
39944      * @param {Object} cfg Xtype definition of item to add.
39945      */
39946     addxtype : function(cfg) {
39947         return this.layout.addxtype(cfg);
39948     
39949     }
39950 });/*
39951  * Based on:
39952  * Ext JS Library 1.1.1
39953  * Copyright(c) 2006-2007, Ext JS, LLC.
39954  *
39955  * Originally Released Under LGPL - original licence link has changed is not relivant.
39956  *
39957  * Fork - LGPL
39958  * <script type="text/javascript">
39959  */
39960 /**
39961  * @class Roo.TabPanel
39962  * @extends Roo.util.Observable
39963  * A lightweight tab container.
39964  * <br><br>
39965  * Usage:
39966  * <pre><code>
39967 // basic tabs 1, built from existing content
39968 var tabs = new Roo.TabPanel("tabs1");
39969 tabs.addTab("script", "View Script");
39970 tabs.addTab("markup", "View Markup");
39971 tabs.activate("script");
39972
39973 // more advanced tabs, built from javascript
39974 var jtabs = new Roo.TabPanel("jtabs");
39975 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39976
39977 // set up the UpdateManager
39978 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39979 var updater = tab2.getUpdateManager();
39980 updater.setDefaultUrl("ajax1.htm");
39981 tab2.on('activate', updater.refresh, updater, true);
39982
39983 // Use setUrl for Ajax loading
39984 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39985 tab3.setUrl("ajax2.htm", null, true);
39986
39987 // Disabled tab
39988 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39989 tab4.disable();
39990
39991 jtabs.activate("jtabs-1");
39992  * </code></pre>
39993  * @constructor
39994  * Create a new TabPanel.
39995  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39996  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39997  */
39998 Roo.bootstrap.panel.Tabs = function(config){
39999     /**
40000     * The container element for this TabPanel.
40001     * @type Roo.Element
40002     */
40003     this.el = Roo.get(config.el);
40004     delete config.el;
40005     if(config){
40006         if(typeof config == "boolean"){
40007             this.tabPosition = config ? "bottom" : "top";
40008         }else{
40009             Roo.apply(this, config);
40010         }
40011     }
40012     
40013     if(this.tabPosition == "bottom"){
40014         // if tabs are at the bottom = create the body first.
40015         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40016         this.el.addClass("roo-tabs-bottom");
40017     }
40018     // next create the tabs holders
40019     
40020     if (this.tabPosition == "west"){
40021         
40022         var reg = this.region; // fake it..
40023         while (reg) {
40024             if (!reg.mgr.parent) {
40025                 break;
40026             }
40027             reg = reg.mgr.parent.region;
40028         }
40029         Roo.log("got nest?");
40030         Roo.log(reg);
40031         if (reg.mgr.getRegion('west')) {
40032             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40033             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40034             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40035             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40036             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40037         
40038             
40039         }
40040         
40041         
40042     } else {
40043      
40044         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40045         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40046         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40047         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40048     }
40049     
40050     
40051     if(Roo.isIE){
40052         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40053     }
40054     
40055     // finally - if tabs are at the top, then create the body last..
40056     if(this.tabPosition != "bottom"){
40057         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40058          * @type Roo.Element
40059          */
40060         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40061         this.el.addClass("roo-tabs-top");
40062     }
40063     this.items = [];
40064
40065     this.bodyEl.setStyle("position", "relative");
40066
40067     this.active = null;
40068     this.activateDelegate = this.activate.createDelegate(this);
40069
40070     this.addEvents({
40071         /**
40072          * @event tabchange
40073          * Fires when the active tab changes
40074          * @param {Roo.TabPanel} this
40075          * @param {Roo.TabPanelItem} activePanel The new active tab
40076          */
40077         "tabchange": true,
40078         /**
40079          * @event beforetabchange
40080          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40081          * @param {Roo.TabPanel} this
40082          * @param {Object} e Set cancel to true on this object to cancel the tab change
40083          * @param {Roo.TabPanelItem} tab The tab being changed to
40084          */
40085         "beforetabchange" : true
40086     });
40087
40088     Roo.EventManager.onWindowResize(this.onResize, this);
40089     this.cpad = this.el.getPadding("lr");
40090     this.hiddenCount = 0;
40091
40092
40093     // toolbar on the tabbar support...
40094     if (this.toolbar) {
40095         alert("no toolbar support yet");
40096         this.toolbar  = false;
40097         /*
40098         var tcfg = this.toolbar;
40099         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40100         this.toolbar = new Roo.Toolbar(tcfg);
40101         if (Roo.isSafari) {
40102             var tbl = tcfg.container.child('table', true);
40103             tbl.setAttribute('width', '100%');
40104         }
40105         */
40106         
40107     }
40108    
40109
40110
40111     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40112 };
40113
40114 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40115     /*
40116      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40117      */
40118     tabPosition : "top",
40119     /*
40120      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40121      */
40122     currentTabWidth : 0,
40123     /*
40124      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40125      */
40126     minTabWidth : 40,
40127     /*
40128      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40129      */
40130     maxTabWidth : 250,
40131     /*
40132      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40133      */
40134     preferredTabWidth : 175,
40135     /*
40136      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40137      */
40138     resizeTabs : false,
40139     /*
40140      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40141      */
40142     monitorResize : true,
40143     /*
40144      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40145      */
40146     toolbar : false,  // set by caller..
40147     
40148     region : false, /// set by caller
40149     
40150     disableTooltips : true, // not used yet...
40151
40152     /**
40153      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40154      * @param {String} id The id of the div to use <b>or create</b>
40155      * @param {String} text The text for the tab
40156      * @param {String} content (optional) Content to put in the TabPanelItem body
40157      * @param {Boolean} closable (optional) True to create a close icon on the tab
40158      * @return {Roo.TabPanelItem} The created TabPanelItem
40159      */
40160     addTab : function(id, text, content, closable, tpl)
40161     {
40162         var item = new Roo.bootstrap.panel.TabItem({
40163             panel: this,
40164             id : id,
40165             text : text,
40166             closable : closable,
40167             tpl : tpl
40168         });
40169         this.addTabItem(item);
40170         if(content){
40171             item.setContent(content);
40172         }
40173         return item;
40174     },
40175
40176     /**
40177      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40178      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40179      * @return {Roo.TabPanelItem}
40180      */
40181     getTab : function(id){
40182         return this.items[id];
40183     },
40184
40185     /**
40186      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40187      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40188      */
40189     hideTab : function(id){
40190         var t = this.items[id];
40191         if(!t.isHidden()){
40192            t.setHidden(true);
40193            this.hiddenCount++;
40194            this.autoSizeTabs();
40195         }
40196     },
40197
40198     /**
40199      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40200      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40201      */
40202     unhideTab : function(id){
40203         var t = this.items[id];
40204         if(t.isHidden()){
40205            t.setHidden(false);
40206            this.hiddenCount--;
40207            this.autoSizeTabs();
40208         }
40209     },
40210
40211     /**
40212      * Adds an existing {@link Roo.TabPanelItem}.
40213      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40214      */
40215     addTabItem : function(item)
40216     {
40217         this.items[item.id] = item;
40218         this.items.push(item);
40219         this.autoSizeTabs();
40220       //  if(this.resizeTabs){
40221     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40222   //         this.autoSizeTabs();
40223 //        }else{
40224 //            item.autoSize();
40225        // }
40226     },
40227
40228     /**
40229      * Removes a {@link Roo.TabPanelItem}.
40230      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40231      */
40232     removeTab : function(id){
40233         var items = this.items;
40234         var tab = items[id];
40235         if(!tab) { return; }
40236         var index = items.indexOf(tab);
40237         if(this.active == tab && items.length > 1){
40238             var newTab = this.getNextAvailable(index);
40239             if(newTab) {
40240                 newTab.activate();
40241             }
40242         }
40243         this.stripEl.dom.removeChild(tab.pnode.dom);
40244         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40245             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40246         }
40247         items.splice(index, 1);
40248         delete this.items[tab.id];
40249         tab.fireEvent("close", tab);
40250         tab.purgeListeners();
40251         this.autoSizeTabs();
40252     },
40253
40254     getNextAvailable : function(start){
40255         var items = this.items;
40256         var index = start;
40257         // look for a next tab that will slide over to
40258         // replace the one being removed
40259         while(index < items.length){
40260             var item = items[++index];
40261             if(item && !item.isHidden()){
40262                 return item;
40263             }
40264         }
40265         // if one isn't found select the previous tab (on the left)
40266         index = start;
40267         while(index >= 0){
40268             var item = items[--index];
40269             if(item && !item.isHidden()){
40270                 return item;
40271             }
40272         }
40273         return null;
40274     },
40275
40276     /**
40277      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40278      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40279      */
40280     disableTab : function(id){
40281         var tab = this.items[id];
40282         if(tab && this.active != tab){
40283             tab.disable();
40284         }
40285     },
40286
40287     /**
40288      * Enables a {@link Roo.TabPanelItem} that is disabled.
40289      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40290      */
40291     enableTab : function(id){
40292         var tab = this.items[id];
40293         tab.enable();
40294     },
40295
40296     /**
40297      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40298      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40299      * @return {Roo.TabPanelItem} The TabPanelItem.
40300      */
40301     activate : function(id)
40302     {
40303         //Roo.log('activite:'  + id);
40304         
40305         var tab = this.items[id];
40306         if(!tab){
40307             return null;
40308         }
40309         if(tab == this.active || tab.disabled){
40310             return tab;
40311         }
40312         var e = {};
40313         this.fireEvent("beforetabchange", this, e, tab);
40314         if(e.cancel !== true && !tab.disabled){
40315             if(this.active){
40316                 this.active.hide();
40317             }
40318             this.active = this.items[id];
40319             this.active.show();
40320             this.fireEvent("tabchange", this, this.active);
40321         }
40322         return tab;
40323     },
40324
40325     /**
40326      * Gets the active {@link Roo.TabPanelItem}.
40327      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40328      */
40329     getActiveTab : function(){
40330         return this.active;
40331     },
40332
40333     /**
40334      * Updates the tab body element to fit the height of the container element
40335      * for overflow scrolling
40336      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40337      */
40338     syncHeight : function(targetHeight){
40339         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40340         var bm = this.bodyEl.getMargins();
40341         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40342         this.bodyEl.setHeight(newHeight);
40343         return newHeight;
40344     },
40345
40346     onResize : function(){
40347         if(this.monitorResize){
40348             this.autoSizeTabs();
40349         }
40350     },
40351
40352     /**
40353      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40354      */
40355     beginUpdate : function(){
40356         this.updating = true;
40357     },
40358
40359     /**
40360      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40361      */
40362     endUpdate : function(){
40363         this.updating = false;
40364         this.autoSizeTabs();
40365     },
40366
40367     /**
40368      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40369      */
40370     autoSizeTabs : function()
40371     {
40372         var count = this.items.length;
40373         var vcount = count - this.hiddenCount;
40374         
40375         if (vcount < 2) {
40376             this.stripEl.hide();
40377         } else {
40378             this.stripEl.show();
40379         }
40380         
40381         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40382             return;
40383         }
40384         
40385         
40386         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40387         var availWidth = Math.floor(w / vcount);
40388         var b = this.stripBody;
40389         if(b.getWidth() > w){
40390             var tabs = this.items;
40391             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40392             if(availWidth < this.minTabWidth){
40393                 /*if(!this.sleft){    // incomplete scrolling code
40394                     this.createScrollButtons();
40395                 }
40396                 this.showScroll();
40397                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40398             }
40399         }else{
40400             if(this.currentTabWidth < this.preferredTabWidth){
40401                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40402             }
40403         }
40404     },
40405
40406     /**
40407      * Returns the number of tabs in this TabPanel.
40408      * @return {Number}
40409      */
40410      getCount : function(){
40411          return this.items.length;
40412      },
40413
40414     /**
40415      * Resizes all the tabs to the passed width
40416      * @param {Number} The new width
40417      */
40418     setTabWidth : function(width){
40419         this.currentTabWidth = width;
40420         for(var i = 0, len = this.items.length; i < len; i++) {
40421                 if(!this.items[i].isHidden()) {
40422                 this.items[i].setWidth(width);
40423             }
40424         }
40425     },
40426
40427     /**
40428      * Destroys this TabPanel
40429      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40430      */
40431     destroy : function(removeEl){
40432         Roo.EventManager.removeResizeListener(this.onResize, this);
40433         for(var i = 0, len = this.items.length; i < len; i++){
40434             this.items[i].purgeListeners();
40435         }
40436         if(removeEl === true){
40437             this.el.update("");
40438             this.el.remove();
40439         }
40440     },
40441     
40442     createStrip : function(container)
40443     {
40444         var strip = document.createElement("nav");
40445         strip.className = Roo.bootstrap.version == 4 ?
40446             "navbar-light bg-light" : 
40447             "navbar navbar-default"; //"x-tabs-wrap";
40448         container.appendChild(strip);
40449         return strip;
40450     },
40451     
40452     createStripList : function(strip)
40453     {
40454         // div wrapper for retard IE
40455         // returns the "tr" element.
40456         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40457         //'<div class="x-tabs-strip-wrap">'+
40458           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40459           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40460         return strip.firstChild; //.firstChild.firstChild.firstChild;
40461     },
40462     createBody : function(container)
40463     {
40464         var body = document.createElement("div");
40465         Roo.id(body, "tab-body");
40466         //Roo.fly(body).addClass("x-tabs-body");
40467         Roo.fly(body).addClass("tab-content");
40468         container.appendChild(body);
40469         return body;
40470     },
40471     createItemBody :function(bodyEl, id){
40472         var body = Roo.getDom(id);
40473         if(!body){
40474             body = document.createElement("div");
40475             body.id = id;
40476         }
40477         //Roo.fly(body).addClass("x-tabs-item-body");
40478         Roo.fly(body).addClass("tab-pane");
40479          bodyEl.insertBefore(body, bodyEl.firstChild);
40480         return body;
40481     },
40482     /** @private */
40483     createStripElements :  function(stripEl, text, closable, tpl)
40484     {
40485         var td = document.createElement("li"); // was td..
40486         td.className = 'nav-item';
40487         
40488         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40489         
40490         
40491         stripEl.appendChild(td);
40492         /*if(closable){
40493             td.className = "x-tabs-closable";
40494             if(!this.closeTpl){
40495                 this.closeTpl = new Roo.Template(
40496                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40497                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40498                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40499                 );
40500             }
40501             var el = this.closeTpl.overwrite(td, {"text": text});
40502             var close = el.getElementsByTagName("div")[0];
40503             var inner = el.getElementsByTagName("em")[0];
40504             return {"el": el, "close": close, "inner": inner};
40505         } else {
40506         */
40507         // not sure what this is..
40508 //            if(!this.tabTpl){
40509                 //this.tabTpl = new Roo.Template(
40510                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40511                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40512                 //);
40513 //                this.tabTpl = new Roo.Template(
40514 //                   '<a href="#">' +
40515 //                   '<span unselectable="on"' +
40516 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40517 //                            ' >{text}</span></a>'
40518 //                );
40519 //                
40520 //            }
40521
40522
40523             var template = tpl || this.tabTpl || false;
40524             
40525             if(!template){
40526                 template =  new Roo.Template(
40527                         Roo.bootstrap.version == 4 ? 
40528                             (
40529                                 '<a class="nav-link" href="#" unselectable="on"' +
40530                                      (this.disableTooltips ? '' : ' title="{text}"') +
40531                                      ' >{text}</a>'
40532                             ) : (
40533                                 '<a class="nav-link" href="#">' +
40534                                 '<span unselectable="on"' +
40535                                          (this.disableTooltips ? '' : ' title="{text}"') +
40536                                     ' >{text}</span></a>'
40537                             )
40538                 );
40539             }
40540             
40541             switch (typeof(template)) {
40542                 case 'object' :
40543                     break;
40544                 case 'string' :
40545                     template = new Roo.Template(template);
40546                     break;
40547                 default :
40548                     break;
40549             }
40550             
40551             var el = template.overwrite(td, {"text": text});
40552             
40553             var inner = el.getElementsByTagName("span")[0];
40554             
40555             return {"el": el, "inner": inner};
40556             
40557     }
40558         
40559     
40560 });
40561
40562 /**
40563  * @class Roo.TabPanelItem
40564  * @extends Roo.util.Observable
40565  * Represents an individual item (tab plus body) in a TabPanel.
40566  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40567  * @param {String} id The id of this TabPanelItem
40568  * @param {String} text The text for the tab of this TabPanelItem
40569  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40570  */
40571 Roo.bootstrap.panel.TabItem = function(config){
40572     /**
40573      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40574      * @type Roo.TabPanel
40575      */
40576     this.tabPanel = config.panel;
40577     /**
40578      * The id for this TabPanelItem
40579      * @type String
40580      */
40581     this.id = config.id;
40582     /** @private */
40583     this.disabled = false;
40584     /** @private */
40585     this.text = config.text;
40586     /** @private */
40587     this.loaded = false;
40588     this.closable = config.closable;
40589
40590     /**
40591      * The body element for this TabPanelItem.
40592      * @type Roo.Element
40593      */
40594     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40595     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40596     this.bodyEl.setStyle("display", "block");
40597     this.bodyEl.setStyle("zoom", "1");
40598     //this.hideAction();
40599
40600     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40601     /** @private */
40602     this.el = Roo.get(els.el);
40603     this.inner = Roo.get(els.inner, true);
40604      this.textEl = Roo.bootstrap.version == 4 ?
40605         this.el : Roo.get(this.el.dom.firstChild, true);
40606
40607     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40608     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40609
40610     
40611 //    this.el.on("mousedown", this.onTabMouseDown, this);
40612     this.el.on("click", this.onTabClick, this);
40613     /** @private */
40614     if(config.closable){
40615         var c = Roo.get(els.close, true);
40616         c.dom.title = this.closeText;
40617         c.addClassOnOver("close-over");
40618         c.on("click", this.closeClick, this);
40619      }
40620
40621     this.addEvents({
40622          /**
40623          * @event activate
40624          * Fires when this tab becomes the active tab.
40625          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40626          * @param {Roo.TabPanelItem} this
40627          */
40628         "activate": true,
40629         /**
40630          * @event beforeclose
40631          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40632          * @param {Roo.TabPanelItem} this
40633          * @param {Object} e Set cancel to true on this object to cancel the close.
40634          */
40635         "beforeclose": true,
40636         /**
40637          * @event close
40638          * Fires when this tab is closed.
40639          * @param {Roo.TabPanelItem} this
40640          */
40641          "close": true,
40642         /**
40643          * @event deactivate
40644          * Fires when this tab is no longer the active tab.
40645          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40646          * @param {Roo.TabPanelItem} this
40647          */
40648          "deactivate" : true
40649     });
40650     this.hidden = false;
40651
40652     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40653 };
40654
40655 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40656            {
40657     purgeListeners : function(){
40658        Roo.util.Observable.prototype.purgeListeners.call(this);
40659        this.el.removeAllListeners();
40660     },
40661     /**
40662      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40663      */
40664     show : function(){
40665         this.status_node.addClass("active");
40666         this.showAction();
40667         if(Roo.isOpera){
40668             this.tabPanel.stripWrap.repaint();
40669         }
40670         this.fireEvent("activate", this.tabPanel, this);
40671     },
40672
40673     /**
40674      * Returns true if this tab is the active tab.
40675      * @return {Boolean}
40676      */
40677     isActive : function(){
40678         return this.tabPanel.getActiveTab() == this;
40679     },
40680
40681     /**
40682      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40683      */
40684     hide : function(){
40685         this.status_node.removeClass("active");
40686         this.hideAction();
40687         this.fireEvent("deactivate", this.tabPanel, this);
40688     },
40689
40690     hideAction : function(){
40691         this.bodyEl.hide();
40692         this.bodyEl.setStyle("position", "absolute");
40693         this.bodyEl.setLeft("-20000px");
40694         this.bodyEl.setTop("-20000px");
40695     },
40696
40697     showAction : function(){
40698         this.bodyEl.setStyle("position", "relative");
40699         this.bodyEl.setTop("");
40700         this.bodyEl.setLeft("");
40701         this.bodyEl.show();
40702     },
40703
40704     /**
40705      * Set the tooltip for the tab.
40706      * @param {String} tooltip The tab's tooltip
40707      */
40708     setTooltip : function(text){
40709         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40710             this.textEl.dom.qtip = text;
40711             this.textEl.dom.removeAttribute('title');
40712         }else{
40713             this.textEl.dom.title = text;
40714         }
40715     },
40716
40717     onTabClick : function(e){
40718         e.preventDefault();
40719         this.tabPanel.activate(this.id);
40720     },
40721
40722     onTabMouseDown : function(e){
40723         e.preventDefault();
40724         this.tabPanel.activate(this.id);
40725     },
40726 /*
40727     getWidth : function(){
40728         return this.inner.getWidth();
40729     },
40730
40731     setWidth : function(width){
40732         var iwidth = width - this.linode.getPadding("lr");
40733         this.inner.setWidth(iwidth);
40734         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40735         this.linode.setWidth(width);
40736     },
40737 */
40738     /**
40739      * Show or hide the tab
40740      * @param {Boolean} hidden True to hide or false to show.
40741      */
40742     setHidden : function(hidden){
40743         this.hidden = hidden;
40744         this.linode.setStyle("display", hidden ? "none" : "");
40745     },
40746
40747     /**
40748      * Returns true if this tab is "hidden"
40749      * @return {Boolean}
40750      */
40751     isHidden : function(){
40752         return this.hidden;
40753     },
40754
40755     /**
40756      * Returns the text for this tab
40757      * @return {String}
40758      */
40759     getText : function(){
40760         return this.text;
40761     },
40762     /*
40763     autoSize : function(){
40764         //this.el.beginMeasure();
40765         this.textEl.setWidth(1);
40766         /*
40767          *  #2804 [new] Tabs in Roojs
40768          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40769          */
40770         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40771         //this.el.endMeasure();
40772     //},
40773
40774     /**
40775      * Sets the text for the tab (Note: this also sets the tooltip text)
40776      * @param {String} text The tab's text and tooltip
40777      */
40778     setText : function(text){
40779         this.text = text;
40780         this.textEl.update(text);
40781         this.setTooltip(text);
40782         //if(!this.tabPanel.resizeTabs){
40783         //    this.autoSize();
40784         //}
40785     },
40786     /**
40787      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40788      */
40789     activate : function(){
40790         this.tabPanel.activate(this.id);
40791     },
40792
40793     /**
40794      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40795      */
40796     disable : function(){
40797         if(this.tabPanel.active != this){
40798             this.disabled = true;
40799             this.status_node.addClass("disabled");
40800         }
40801     },
40802
40803     /**
40804      * Enables this TabPanelItem if it was previously disabled.
40805      */
40806     enable : function(){
40807         this.disabled = false;
40808         this.status_node.removeClass("disabled");
40809     },
40810
40811     /**
40812      * Sets the content for this TabPanelItem.
40813      * @param {String} content The content
40814      * @param {Boolean} loadScripts true to look for and load scripts
40815      */
40816     setContent : function(content, loadScripts){
40817         this.bodyEl.update(content, loadScripts);
40818     },
40819
40820     /**
40821      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40822      * @return {Roo.UpdateManager} The UpdateManager
40823      */
40824     getUpdateManager : function(){
40825         return this.bodyEl.getUpdateManager();
40826     },
40827
40828     /**
40829      * Set a URL to be used to load the content for this TabPanelItem.
40830      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40831      * @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)
40832      * @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)
40833      * @return {Roo.UpdateManager} The UpdateManager
40834      */
40835     setUrl : function(url, params, loadOnce){
40836         if(this.refreshDelegate){
40837             this.un('activate', this.refreshDelegate);
40838         }
40839         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40840         this.on("activate", this.refreshDelegate);
40841         return this.bodyEl.getUpdateManager();
40842     },
40843
40844     /** @private */
40845     _handleRefresh : function(url, params, loadOnce){
40846         if(!loadOnce || !this.loaded){
40847             var updater = this.bodyEl.getUpdateManager();
40848             updater.update(url, params, this._setLoaded.createDelegate(this));
40849         }
40850     },
40851
40852     /**
40853      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40854      *   Will fail silently if the setUrl method has not been called.
40855      *   This does not activate the panel, just updates its content.
40856      */
40857     refresh : function(){
40858         if(this.refreshDelegate){
40859            this.loaded = false;
40860            this.refreshDelegate();
40861         }
40862     },
40863
40864     /** @private */
40865     _setLoaded : function(){
40866         this.loaded = true;
40867     },
40868
40869     /** @private */
40870     closeClick : function(e){
40871         var o = {};
40872         e.stopEvent();
40873         this.fireEvent("beforeclose", this, o);
40874         if(o.cancel !== true){
40875             this.tabPanel.removeTab(this.id);
40876         }
40877     },
40878     /**
40879      * The text displayed in the tooltip for the close icon.
40880      * @type String
40881      */
40882     closeText : "Close this tab"
40883 });
40884 /**
40885 *    This script refer to:
40886 *    Title: International Telephone Input
40887 *    Author: Jack O'Connor
40888 *    Code version:  v12.1.12
40889 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40890 **/
40891
40892 Roo.bootstrap.PhoneInputData = function() {
40893     var d = [
40894       [
40895         "Afghanistan (‫افغانستان‬‎)",
40896         "af",
40897         "93"
40898       ],
40899       [
40900         "Albania (Shqipëri)",
40901         "al",
40902         "355"
40903       ],
40904       [
40905         "Algeria (‫الجزائر‬‎)",
40906         "dz",
40907         "213"
40908       ],
40909       [
40910         "American Samoa",
40911         "as",
40912         "1684"
40913       ],
40914       [
40915         "Andorra",
40916         "ad",
40917         "376"
40918       ],
40919       [
40920         "Angola",
40921         "ao",
40922         "244"
40923       ],
40924       [
40925         "Anguilla",
40926         "ai",
40927         "1264"
40928       ],
40929       [
40930         "Antigua and Barbuda",
40931         "ag",
40932         "1268"
40933       ],
40934       [
40935         "Argentina",
40936         "ar",
40937         "54"
40938       ],
40939       [
40940         "Armenia (Հայաստան)",
40941         "am",
40942         "374"
40943       ],
40944       [
40945         "Aruba",
40946         "aw",
40947         "297"
40948       ],
40949       [
40950         "Australia",
40951         "au",
40952         "61",
40953         0
40954       ],
40955       [
40956         "Austria (Österreich)",
40957         "at",
40958         "43"
40959       ],
40960       [
40961         "Azerbaijan (Azərbaycan)",
40962         "az",
40963         "994"
40964       ],
40965       [
40966         "Bahamas",
40967         "bs",
40968         "1242"
40969       ],
40970       [
40971         "Bahrain (‫البحرين‬‎)",
40972         "bh",
40973         "973"
40974       ],
40975       [
40976         "Bangladesh (বাংলাদেশ)",
40977         "bd",
40978         "880"
40979       ],
40980       [
40981         "Barbados",
40982         "bb",
40983         "1246"
40984       ],
40985       [
40986         "Belarus (Беларусь)",
40987         "by",
40988         "375"
40989       ],
40990       [
40991         "Belgium (België)",
40992         "be",
40993         "32"
40994       ],
40995       [
40996         "Belize",
40997         "bz",
40998         "501"
40999       ],
41000       [
41001         "Benin (Bénin)",
41002         "bj",
41003         "229"
41004       ],
41005       [
41006         "Bermuda",
41007         "bm",
41008         "1441"
41009       ],
41010       [
41011         "Bhutan (འབྲུག)",
41012         "bt",
41013         "975"
41014       ],
41015       [
41016         "Bolivia",
41017         "bo",
41018         "591"
41019       ],
41020       [
41021         "Bosnia and Herzegovina (Босна и Херцеговина)",
41022         "ba",
41023         "387"
41024       ],
41025       [
41026         "Botswana",
41027         "bw",
41028         "267"
41029       ],
41030       [
41031         "Brazil (Brasil)",
41032         "br",
41033         "55"
41034       ],
41035       [
41036         "British Indian Ocean Territory",
41037         "io",
41038         "246"
41039       ],
41040       [
41041         "British Virgin Islands",
41042         "vg",
41043         "1284"
41044       ],
41045       [
41046         "Brunei",
41047         "bn",
41048         "673"
41049       ],
41050       [
41051         "Bulgaria (България)",
41052         "bg",
41053         "359"
41054       ],
41055       [
41056         "Burkina Faso",
41057         "bf",
41058         "226"
41059       ],
41060       [
41061         "Burundi (Uburundi)",
41062         "bi",
41063         "257"
41064       ],
41065       [
41066         "Cambodia (កម្ពុជា)",
41067         "kh",
41068         "855"
41069       ],
41070       [
41071         "Cameroon (Cameroun)",
41072         "cm",
41073         "237"
41074       ],
41075       [
41076         "Canada",
41077         "ca",
41078         "1",
41079         1,
41080         ["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"]
41081       ],
41082       [
41083         "Cape Verde (Kabu Verdi)",
41084         "cv",
41085         "238"
41086       ],
41087       [
41088         "Caribbean Netherlands",
41089         "bq",
41090         "599",
41091         1
41092       ],
41093       [
41094         "Cayman Islands",
41095         "ky",
41096         "1345"
41097       ],
41098       [
41099         "Central African Republic (République centrafricaine)",
41100         "cf",
41101         "236"
41102       ],
41103       [
41104         "Chad (Tchad)",
41105         "td",
41106         "235"
41107       ],
41108       [
41109         "Chile",
41110         "cl",
41111         "56"
41112       ],
41113       [
41114         "China (中国)",
41115         "cn",
41116         "86"
41117       ],
41118       [
41119         "Christmas Island",
41120         "cx",
41121         "61",
41122         2
41123       ],
41124       [
41125         "Cocos (Keeling) Islands",
41126         "cc",
41127         "61",
41128         1
41129       ],
41130       [
41131         "Colombia",
41132         "co",
41133         "57"
41134       ],
41135       [
41136         "Comoros (‫جزر القمر‬‎)",
41137         "km",
41138         "269"
41139       ],
41140       [
41141         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41142         "cd",
41143         "243"
41144       ],
41145       [
41146         "Congo (Republic) (Congo-Brazzaville)",
41147         "cg",
41148         "242"
41149       ],
41150       [
41151         "Cook Islands",
41152         "ck",
41153         "682"
41154       ],
41155       [
41156         "Costa Rica",
41157         "cr",
41158         "506"
41159       ],
41160       [
41161         "Côte d’Ivoire",
41162         "ci",
41163         "225"
41164       ],
41165       [
41166         "Croatia (Hrvatska)",
41167         "hr",
41168         "385"
41169       ],
41170       [
41171         "Cuba",
41172         "cu",
41173         "53"
41174       ],
41175       [
41176         "Curaçao",
41177         "cw",
41178         "599",
41179         0
41180       ],
41181       [
41182         "Cyprus (Κύπρος)",
41183         "cy",
41184         "357"
41185       ],
41186       [
41187         "Czech Republic (Česká republika)",
41188         "cz",
41189         "420"
41190       ],
41191       [
41192         "Denmark (Danmark)",
41193         "dk",
41194         "45"
41195       ],
41196       [
41197         "Djibouti",
41198         "dj",
41199         "253"
41200       ],
41201       [
41202         "Dominica",
41203         "dm",
41204         "1767"
41205       ],
41206       [
41207         "Dominican Republic (República Dominicana)",
41208         "do",
41209         "1",
41210         2,
41211         ["809", "829", "849"]
41212       ],
41213       [
41214         "Ecuador",
41215         "ec",
41216         "593"
41217       ],
41218       [
41219         "Egypt (‫مصر‬‎)",
41220         "eg",
41221         "20"
41222       ],
41223       [
41224         "El Salvador",
41225         "sv",
41226         "503"
41227       ],
41228       [
41229         "Equatorial Guinea (Guinea Ecuatorial)",
41230         "gq",
41231         "240"
41232       ],
41233       [
41234         "Eritrea",
41235         "er",
41236         "291"
41237       ],
41238       [
41239         "Estonia (Eesti)",
41240         "ee",
41241         "372"
41242       ],
41243       [
41244         "Ethiopia",
41245         "et",
41246         "251"
41247       ],
41248       [
41249         "Falkland Islands (Islas Malvinas)",
41250         "fk",
41251         "500"
41252       ],
41253       [
41254         "Faroe Islands (Føroyar)",
41255         "fo",
41256         "298"
41257       ],
41258       [
41259         "Fiji",
41260         "fj",
41261         "679"
41262       ],
41263       [
41264         "Finland (Suomi)",
41265         "fi",
41266         "358",
41267         0
41268       ],
41269       [
41270         "France",
41271         "fr",
41272         "33"
41273       ],
41274       [
41275         "French Guiana (Guyane française)",
41276         "gf",
41277         "594"
41278       ],
41279       [
41280         "French Polynesia (Polynésie française)",
41281         "pf",
41282         "689"
41283       ],
41284       [
41285         "Gabon",
41286         "ga",
41287         "241"
41288       ],
41289       [
41290         "Gambia",
41291         "gm",
41292         "220"
41293       ],
41294       [
41295         "Georgia (საქართველო)",
41296         "ge",
41297         "995"
41298       ],
41299       [
41300         "Germany (Deutschland)",
41301         "de",
41302         "49"
41303       ],
41304       [
41305         "Ghana (Gaana)",
41306         "gh",
41307         "233"
41308       ],
41309       [
41310         "Gibraltar",
41311         "gi",
41312         "350"
41313       ],
41314       [
41315         "Greece (Ελλάδα)",
41316         "gr",
41317         "30"
41318       ],
41319       [
41320         "Greenland (Kalaallit Nunaat)",
41321         "gl",
41322         "299"
41323       ],
41324       [
41325         "Grenada",
41326         "gd",
41327         "1473"
41328       ],
41329       [
41330         "Guadeloupe",
41331         "gp",
41332         "590",
41333         0
41334       ],
41335       [
41336         "Guam",
41337         "gu",
41338         "1671"
41339       ],
41340       [
41341         "Guatemala",
41342         "gt",
41343         "502"
41344       ],
41345       [
41346         "Guernsey",
41347         "gg",
41348         "44",
41349         1
41350       ],
41351       [
41352         "Guinea (Guinée)",
41353         "gn",
41354         "224"
41355       ],
41356       [
41357         "Guinea-Bissau (Guiné Bissau)",
41358         "gw",
41359         "245"
41360       ],
41361       [
41362         "Guyana",
41363         "gy",
41364         "592"
41365       ],
41366       [
41367         "Haiti",
41368         "ht",
41369         "509"
41370       ],
41371       [
41372         "Honduras",
41373         "hn",
41374         "504"
41375       ],
41376       [
41377         "Hong Kong (香港)",
41378         "hk",
41379         "852"
41380       ],
41381       [
41382         "Hungary (Magyarország)",
41383         "hu",
41384         "36"
41385       ],
41386       [
41387         "Iceland (Ísland)",
41388         "is",
41389         "354"
41390       ],
41391       [
41392         "India (भारत)",
41393         "in",
41394         "91"
41395       ],
41396       [
41397         "Indonesia",
41398         "id",
41399         "62"
41400       ],
41401       [
41402         "Iran (‫ایران‬‎)",
41403         "ir",
41404         "98"
41405       ],
41406       [
41407         "Iraq (‫العراق‬‎)",
41408         "iq",
41409         "964"
41410       ],
41411       [
41412         "Ireland",
41413         "ie",
41414         "353"
41415       ],
41416       [
41417         "Isle of Man",
41418         "im",
41419         "44",
41420         2
41421       ],
41422       [
41423         "Israel (‫ישראל‬‎)",
41424         "il",
41425         "972"
41426       ],
41427       [
41428         "Italy (Italia)",
41429         "it",
41430         "39",
41431         0
41432       ],
41433       [
41434         "Jamaica",
41435         "jm",
41436         "1876"
41437       ],
41438       [
41439         "Japan (日本)",
41440         "jp",
41441         "81"
41442       ],
41443       [
41444         "Jersey",
41445         "je",
41446         "44",
41447         3
41448       ],
41449       [
41450         "Jordan (‫الأردن‬‎)",
41451         "jo",
41452         "962"
41453       ],
41454       [
41455         "Kazakhstan (Казахстан)",
41456         "kz",
41457         "7",
41458         1
41459       ],
41460       [
41461         "Kenya",
41462         "ke",
41463         "254"
41464       ],
41465       [
41466         "Kiribati",
41467         "ki",
41468         "686"
41469       ],
41470       [
41471         "Kosovo",
41472         "xk",
41473         "383"
41474       ],
41475       [
41476         "Kuwait (‫الكويت‬‎)",
41477         "kw",
41478         "965"
41479       ],
41480       [
41481         "Kyrgyzstan (Кыргызстан)",
41482         "kg",
41483         "996"
41484       ],
41485       [
41486         "Laos (ລາວ)",
41487         "la",
41488         "856"
41489       ],
41490       [
41491         "Latvia (Latvija)",
41492         "lv",
41493         "371"
41494       ],
41495       [
41496         "Lebanon (‫لبنان‬‎)",
41497         "lb",
41498         "961"
41499       ],
41500       [
41501         "Lesotho",
41502         "ls",
41503         "266"
41504       ],
41505       [
41506         "Liberia",
41507         "lr",
41508         "231"
41509       ],
41510       [
41511         "Libya (‫ليبيا‬‎)",
41512         "ly",
41513         "218"
41514       ],
41515       [
41516         "Liechtenstein",
41517         "li",
41518         "423"
41519       ],
41520       [
41521         "Lithuania (Lietuva)",
41522         "lt",
41523         "370"
41524       ],
41525       [
41526         "Luxembourg",
41527         "lu",
41528         "352"
41529       ],
41530       [
41531         "Macau (澳門)",
41532         "mo",
41533         "853"
41534       ],
41535       [
41536         "Macedonia (FYROM) (Македонија)",
41537         "mk",
41538         "389"
41539       ],
41540       [
41541         "Madagascar (Madagasikara)",
41542         "mg",
41543         "261"
41544       ],
41545       [
41546         "Malawi",
41547         "mw",
41548         "265"
41549       ],
41550       [
41551         "Malaysia",
41552         "my",
41553         "60"
41554       ],
41555       [
41556         "Maldives",
41557         "mv",
41558         "960"
41559       ],
41560       [
41561         "Mali",
41562         "ml",
41563         "223"
41564       ],
41565       [
41566         "Malta",
41567         "mt",
41568         "356"
41569       ],
41570       [
41571         "Marshall Islands",
41572         "mh",
41573         "692"
41574       ],
41575       [
41576         "Martinique",
41577         "mq",
41578         "596"
41579       ],
41580       [
41581         "Mauritania (‫موريتانيا‬‎)",
41582         "mr",
41583         "222"
41584       ],
41585       [
41586         "Mauritius (Moris)",
41587         "mu",
41588         "230"
41589       ],
41590       [
41591         "Mayotte",
41592         "yt",
41593         "262",
41594         1
41595       ],
41596       [
41597         "Mexico (México)",
41598         "mx",
41599         "52"
41600       ],
41601       [
41602         "Micronesia",
41603         "fm",
41604         "691"
41605       ],
41606       [
41607         "Moldova (Republica Moldova)",
41608         "md",
41609         "373"
41610       ],
41611       [
41612         "Monaco",
41613         "mc",
41614         "377"
41615       ],
41616       [
41617         "Mongolia (Монгол)",
41618         "mn",
41619         "976"
41620       ],
41621       [
41622         "Montenegro (Crna Gora)",
41623         "me",
41624         "382"
41625       ],
41626       [
41627         "Montserrat",
41628         "ms",
41629         "1664"
41630       ],
41631       [
41632         "Morocco (‫المغرب‬‎)",
41633         "ma",
41634         "212",
41635         0
41636       ],
41637       [
41638         "Mozambique (Moçambique)",
41639         "mz",
41640         "258"
41641       ],
41642       [
41643         "Myanmar (Burma) (မြန်မာ)",
41644         "mm",
41645         "95"
41646       ],
41647       [
41648         "Namibia (Namibië)",
41649         "na",
41650         "264"
41651       ],
41652       [
41653         "Nauru",
41654         "nr",
41655         "674"
41656       ],
41657       [
41658         "Nepal (नेपाल)",
41659         "np",
41660         "977"
41661       ],
41662       [
41663         "Netherlands (Nederland)",
41664         "nl",
41665         "31"
41666       ],
41667       [
41668         "New Caledonia (Nouvelle-Calédonie)",
41669         "nc",
41670         "687"
41671       ],
41672       [
41673         "New Zealand",
41674         "nz",
41675         "64"
41676       ],
41677       [
41678         "Nicaragua",
41679         "ni",
41680         "505"
41681       ],
41682       [
41683         "Niger (Nijar)",
41684         "ne",
41685         "227"
41686       ],
41687       [
41688         "Nigeria",
41689         "ng",
41690         "234"
41691       ],
41692       [
41693         "Niue",
41694         "nu",
41695         "683"
41696       ],
41697       [
41698         "Norfolk Island",
41699         "nf",
41700         "672"
41701       ],
41702       [
41703         "North Korea (조선 민주주의 인민 공화국)",
41704         "kp",
41705         "850"
41706       ],
41707       [
41708         "Northern Mariana Islands",
41709         "mp",
41710         "1670"
41711       ],
41712       [
41713         "Norway (Norge)",
41714         "no",
41715         "47",
41716         0
41717       ],
41718       [
41719         "Oman (‫عُمان‬‎)",
41720         "om",
41721         "968"
41722       ],
41723       [
41724         "Pakistan (‫پاکستان‬‎)",
41725         "pk",
41726         "92"
41727       ],
41728       [
41729         "Palau",
41730         "pw",
41731         "680"
41732       ],
41733       [
41734         "Palestine (‫فلسطين‬‎)",
41735         "ps",
41736         "970"
41737       ],
41738       [
41739         "Panama (Panamá)",
41740         "pa",
41741         "507"
41742       ],
41743       [
41744         "Papua New Guinea",
41745         "pg",
41746         "675"
41747       ],
41748       [
41749         "Paraguay",
41750         "py",
41751         "595"
41752       ],
41753       [
41754         "Peru (Perú)",
41755         "pe",
41756         "51"
41757       ],
41758       [
41759         "Philippines",
41760         "ph",
41761         "63"
41762       ],
41763       [
41764         "Poland (Polska)",
41765         "pl",
41766         "48"
41767       ],
41768       [
41769         "Portugal",
41770         "pt",
41771         "351"
41772       ],
41773       [
41774         "Puerto Rico",
41775         "pr",
41776         "1",
41777         3,
41778         ["787", "939"]
41779       ],
41780       [
41781         "Qatar (‫قطر‬‎)",
41782         "qa",
41783         "974"
41784       ],
41785       [
41786         "Réunion (La Réunion)",
41787         "re",
41788         "262",
41789         0
41790       ],
41791       [
41792         "Romania (România)",
41793         "ro",
41794         "40"
41795       ],
41796       [
41797         "Russia (Россия)",
41798         "ru",
41799         "7",
41800         0
41801       ],
41802       [
41803         "Rwanda",
41804         "rw",
41805         "250"
41806       ],
41807       [
41808         "Saint Barthélemy",
41809         "bl",
41810         "590",
41811         1
41812       ],
41813       [
41814         "Saint Helena",
41815         "sh",
41816         "290"
41817       ],
41818       [
41819         "Saint Kitts and Nevis",
41820         "kn",
41821         "1869"
41822       ],
41823       [
41824         "Saint Lucia",
41825         "lc",
41826         "1758"
41827       ],
41828       [
41829         "Saint Martin (Saint-Martin (partie française))",
41830         "mf",
41831         "590",
41832         2
41833       ],
41834       [
41835         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41836         "pm",
41837         "508"
41838       ],
41839       [
41840         "Saint Vincent and the Grenadines",
41841         "vc",
41842         "1784"
41843       ],
41844       [
41845         "Samoa",
41846         "ws",
41847         "685"
41848       ],
41849       [
41850         "San Marino",
41851         "sm",
41852         "378"
41853       ],
41854       [
41855         "São Tomé and Príncipe (São Tomé e Príncipe)",
41856         "st",
41857         "239"
41858       ],
41859       [
41860         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41861         "sa",
41862         "966"
41863       ],
41864       [
41865         "Senegal (Sénégal)",
41866         "sn",
41867         "221"
41868       ],
41869       [
41870         "Serbia (Србија)",
41871         "rs",
41872         "381"
41873       ],
41874       [
41875         "Seychelles",
41876         "sc",
41877         "248"
41878       ],
41879       [
41880         "Sierra Leone",
41881         "sl",
41882         "232"
41883       ],
41884       [
41885         "Singapore",
41886         "sg",
41887         "65"
41888       ],
41889       [
41890         "Sint Maarten",
41891         "sx",
41892         "1721"
41893       ],
41894       [
41895         "Slovakia (Slovensko)",
41896         "sk",
41897         "421"
41898       ],
41899       [
41900         "Slovenia (Slovenija)",
41901         "si",
41902         "386"
41903       ],
41904       [
41905         "Solomon Islands",
41906         "sb",
41907         "677"
41908       ],
41909       [
41910         "Somalia (Soomaaliya)",
41911         "so",
41912         "252"
41913       ],
41914       [
41915         "South Africa",
41916         "za",
41917         "27"
41918       ],
41919       [
41920         "South Korea (대한민국)",
41921         "kr",
41922         "82"
41923       ],
41924       [
41925         "South Sudan (‫جنوب السودان‬‎)",
41926         "ss",
41927         "211"
41928       ],
41929       [
41930         "Spain (España)",
41931         "es",
41932         "34"
41933       ],
41934       [
41935         "Sri Lanka (ශ්‍රී ලංකාව)",
41936         "lk",
41937         "94"
41938       ],
41939       [
41940         "Sudan (‫السودان‬‎)",
41941         "sd",
41942         "249"
41943       ],
41944       [
41945         "Suriname",
41946         "sr",
41947         "597"
41948       ],
41949       [
41950         "Svalbard and Jan Mayen",
41951         "sj",
41952         "47",
41953         1
41954       ],
41955       [
41956         "Swaziland",
41957         "sz",
41958         "268"
41959       ],
41960       [
41961         "Sweden (Sverige)",
41962         "se",
41963         "46"
41964       ],
41965       [
41966         "Switzerland (Schweiz)",
41967         "ch",
41968         "41"
41969       ],
41970       [
41971         "Syria (‫سوريا‬‎)",
41972         "sy",
41973         "963"
41974       ],
41975       [
41976         "Taiwan (台灣)",
41977         "tw",
41978         "886"
41979       ],
41980       [
41981         "Tajikistan",
41982         "tj",
41983         "992"
41984       ],
41985       [
41986         "Tanzania",
41987         "tz",
41988         "255"
41989       ],
41990       [
41991         "Thailand (ไทย)",
41992         "th",
41993         "66"
41994       ],
41995       [
41996         "Timor-Leste",
41997         "tl",
41998         "670"
41999       ],
42000       [
42001         "Togo",
42002         "tg",
42003         "228"
42004       ],
42005       [
42006         "Tokelau",
42007         "tk",
42008         "690"
42009       ],
42010       [
42011         "Tonga",
42012         "to",
42013         "676"
42014       ],
42015       [
42016         "Trinidad and Tobago",
42017         "tt",
42018         "1868"
42019       ],
42020       [
42021         "Tunisia (‫تونس‬‎)",
42022         "tn",
42023         "216"
42024       ],
42025       [
42026         "Turkey (Türkiye)",
42027         "tr",
42028         "90"
42029       ],
42030       [
42031         "Turkmenistan",
42032         "tm",
42033         "993"
42034       ],
42035       [
42036         "Turks and Caicos Islands",
42037         "tc",
42038         "1649"
42039       ],
42040       [
42041         "Tuvalu",
42042         "tv",
42043         "688"
42044       ],
42045       [
42046         "U.S. Virgin Islands",
42047         "vi",
42048         "1340"
42049       ],
42050       [
42051         "Uganda",
42052         "ug",
42053         "256"
42054       ],
42055       [
42056         "Ukraine (Україна)",
42057         "ua",
42058         "380"
42059       ],
42060       [
42061         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42062         "ae",
42063         "971"
42064       ],
42065       [
42066         "United Kingdom",
42067         "gb",
42068         "44",
42069         0
42070       ],
42071       [
42072         "United States",
42073         "us",
42074         "1",
42075         0
42076       ],
42077       [
42078         "Uruguay",
42079         "uy",
42080         "598"
42081       ],
42082       [
42083         "Uzbekistan (Oʻzbekiston)",
42084         "uz",
42085         "998"
42086       ],
42087       [
42088         "Vanuatu",
42089         "vu",
42090         "678"
42091       ],
42092       [
42093         "Vatican City (Città del Vaticano)",
42094         "va",
42095         "39",
42096         1
42097       ],
42098       [
42099         "Venezuela",
42100         "ve",
42101         "58"
42102       ],
42103       [
42104         "Vietnam (Việt Nam)",
42105         "vn",
42106         "84"
42107       ],
42108       [
42109         "Wallis and Futuna (Wallis-et-Futuna)",
42110         "wf",
42111         "681"
42112       ],
42113       [
42114         "Western Sahara (‫الصحراء الغربية‬‎)",
42115         "eh",
42116         "212",
42117         1
42118       ],
42119       [
42120         "Yemen (‫اليمن‬‎)",
42121         "ye",
42122         "967"
42123       ],
42124       [
42125         "Zambia",
42126         "zm",
42127         "260"
42128       ],
42129       [
42130         "Zimbabwe",
42131         "zw",
42132         "263"
42133       ],
42134       [
42135         "Åland Islands",
42136         "ax",
42137         "358",
42138         1
42139       ]
42140   ];
42141   
42142   return d;
42143 }/**
42144 *    This script refer to:
42145 *    Title: International Telephone Input
42146 *    Author: Jack O'Connor
42147 *    Code version:  v12.1.12
42148 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42149 **/
42150
42151 /**
42152  * @class Roo.bootstrap.PhoneInput
42153  * @extends Roo.bootstrap.TriggerField
42154  * An input with International dial-code selection
42155  
42156  * @cfg {String} defaultDialCode default '+852'
42157  * @cfg {Array} preferedCountries default []
42158   
42159  * @constructor
42160  * Create a new PhoneInput.
42161  * @param {Object} config Configuration options
42162  */
42163
42164 Roo.bootstrap.PhoneInput = function(config) {
42165     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42166 };
42167
42168 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42169         
42170         listWidth: undefined,
42171         
42172         selectedClass: 'active',
42173         
42174         invalidClass : "has-warning",
42175         
42176         validClass: 'has-success',
42177         
42178         allowed: '0123456789',
42179         
42180         max_length: 15,
42181         
42182         /**
42183          * @cfg {String} defaultDialCode The default dial code when initializing the input
42184          */
42185         defaultDialCode: '+852',
42186         
42187         /**
42188          * @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
42189          */
42190         preferedCountries: false,
42191         
42192         getAutoCreate : function()
42193         {
42194             var data = Roo.bootstrap.PhoneInputData();
42195             var align = this.labelAlign || this.parentLabelAlign();
42196             var id = Roo.id();
42197             
42198             this.allCountries = [];
42199             this.dialCodeMapping = [];
42200             
42201             for (var i = 0; i < data.length; i++) {
42202               var c = data[i];
42203               this.allCountries[i] = {
42204                 name: c[0],
42205                 iso2: c[1],
42206                 dialCode: c[2],
42207                 priority: c[3] || 0,
42208                 areaCodes: c[4] || null
42209               };
42210               this.dialCodeMapping[c[2]] = {
42211                   name: c[0],
42212                   iso2: c[1],
42213                   priority: c[3] || 0,
42214                   areaCodes: c[4] || null
42215               };
42216             }
42217             
42218             var cfg = {
42219                 cls: 'form-group',
42220                 cn: []
42221             };
42222             
42223             var input =  {
42224                 tag: 'input',
42225                 id : id,
42226                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42227                 maxlength: this.max_length,
42228                 cls : 'form-control tel-input',
42229                 autocomplete: 'new-password'
42230             };
42231             
42232             var hiddenInput = {
42233                 tag: 'input',
42234                 type: 'hidden',
42235                 cls: 'hidden-tel-input'
42236             };
42237             
42238             if (this.name) {
42239                 hiddenInput.name = this.name;
42240             }
42241             
42242             if (this.disabled) {
42243                 input.disabled = true;
42244             }
42245             
42246             var flag_container = {
42247                 tag: 'div',
42248                 cls: 'flag-box',
42249                 cn: [
42250                     {
42251                         tag: 'div',
42252                         cls: 'flag'
42253                     },
42254                     {
42255                         tag: 'div',
42256                         cls: 'caret'
42257                     }
42258                 ]
42259             };
42260             
42261             var box = {
42262                 tag: 'div',
42263                 cls: this.hasFeedback ? 'has-feedback' : '',
42264                 cn: [
42265                     hiddenInput,
42266                     input,
42267                     {
42268                         tag: 'input',
42269                         cls: 'dial-code-holder',
42270                         disabled: true
42271                     }
42272                 ]
42273             };
42274             
42275             var container = {
42276                 cls: 'roo-select2-container input-group',
42277                 cn: [
42278                     flag_container,
42279                     box
42280                 ]
42281             };
42282             
42283             if (this.fieldLabel.length) {
42284                 var indicator = {
42285                     tag: 'i',
42286                     tooltip: 'This field is required'
42287                 };
42288                 
42289                 var label = {
42290                     tag: 'label',
42291                     'for':  id,
42292                     cls: 'control-label',
42293                     cn: []
42294                 };
42295                 
42296                 var label_text = {
42297                     tag: 'span',
42298                     html: this.fieldLabel
42299                 };
42300                 
42301                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42302                 label.cn = [
42303                     indicator,
42304                     label_text
42305                 ];
42306                 
42307                 if(this.indicatorpos == 'right') {
42308                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42309                     label.cn = [
42310                         label_text,
42311                         indicator
42312                     ];
42313                 }
42314                 
42315                 if(align == 'left') {
42316                     container = {
42317                         tag: 'div',
42318                         cn: [
42319                             container
42320                         ]
42321                     };
42322                     
42323                     if(this.labelWidth > 12){
42324                         label.style = "width: " + this.labelWidth + 'px';
42325                     }
42326                     if(this.labelWidth < 13 && this.labelmd == 0){
42327                         this.labelmd = this.labelWidth;
42328                     }
42329                     if(this.labellg > 0){
42330                         label.cls += ' col-lg-' + this.labellg;
42331                         input.cls += ' col-lg-' + (12 - this.labellg);
42332                     }
42333                     if(this.labelmd > 0){
42334                         label.cls += ' col-md-' + this.labelmd;
42335                         container.cls += ' col-md-' + (12 - this.labelmd);
42336                     }
42337                     if(this.labelsm > 0){
42338                         label.cls += ' col-sm-' + this.labelsm;
42339                         container.cls += ' col-sm-' + (12 - this.labelsm);
42340                     }
42341                     if(this.labelxs > 0){
42342                         label.cls += ' col-xs-' + this.labelxs;
42343                         container.cls += ' col-xs-' + (12 - this.labelxs);
42344                     }
42345                 }
42346             }
42347             
42348             cfg.cn = [
42349                 label,
42350                 container
42351             ];
42352             
42353             var settings = this;
42354             
42355             ['xs','sm','md','lg'].map(function(size){
42356                 if (settings[size]) {
42357                     cfg.cls += ' col-' + size + '-' + settings[size];
42358                 }
42359             });
42360             
42361             this.store = new Roo.data.Store({
42362                 proxy : new Roo.data.MemoryProxy({}),
42363                 reader : new Roo.data.JsonReader({
42364                     fields : [
42365                         {
42366                             'name' : 'name',
42367                             'type' : 'string'
42368                         },
42369                         {
42370                             'name' : 'iso2',
42371                             'type' : 'string'
42372                         },
42373                         {
42374                             'name' : 'dialCode',
42375                             'type' : 'string'
42376                         },
42377                         {
42378                             'name' : 'priority',
42379                             'type' : 'string'
42380                         },
42381                         {
42382                             'name' : 'areaCodes',
42383                             'type' : 'string'
42384                         }
42385                     ]
42386                 })
42387             });
42388             
42389             if(!this.preferedCountries) {
42390                 this.preferedCountries = [
42391                     'hk',
42392                     'gb',
42393                     'us'
42394                 ];
42395             }
42396             
42397             var p = this.preferedCountries.reverse();
42398             
42399             if(p) {
42400                 for (var i = 0; i < p.length; i++) {
42401                     for (var j = 0; j < this.allCountries.length; j++) {
42402                         if(this.allCountries[j].iso2 == p[i]) {
42403                             var t = this.allCountries[j];
42404                             this.allCountries.splice(j,1);
42405                             this.allCountries.unshift(t);
42406                         }
42407                     } 
42408                 }
42409             }
42410             
42411             this.store.proxy.data = {
42412                 success: true,
42413                 data: this.allCountries
42414             };
42415             
42416             return cfg;
42417         },
42418         
42419         initEvents : function()
42420         {
42421             this.createList();
42422             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42423             
42424             this.indicator = this.indicatorEl();
42425             this.flag = this.flagEl();
42426             this.dialCodeHolder = this.dialCodeHolderEl();
42427             
42428             this.trigger = this.el.select('div.flag-box',true).first();
42429             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42430             
42431             var _this = this;
42432             
42433             (function(){
42434                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42435                 _this.list.setWidth(lw);
42436             }).defer(100);
42437             
42438             this.list.on('mouseover', this.onViewOver, this);
42439             this.list.on('mousemove', this.onViewMove, this);
42440             this.inputEl().on("keyup", this.onKeyUp, this);
42441             this.inputEl().on("keypress", this.onKeyPress, this);
42442             
42443             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42444
42445             this.view = new Roo.View(this.list, this.tpl, {
42446                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42447             });
42448             
42449             this.view.on('click', this.onViewClick, this);
42450             this.setValue(this.defaultDialCode);
42451         },
42452         
42453         onTriggerClick : function(e)
42454         {
42455             Roo.log('trigger click');
42456             if(this.disabled){
42457                 return;
42458             }
42459             
42460             if(this.isExpanded()){
42461                 this.collapse();
42462                 this.hasFocus = false;
42463             }else {
42464                 this.store.load({});
42465                 this.hasFocus = true;
42466                 this.expand();
42467             }
42468         },
42469         
42470         isExpanded : function()
42471         {
42472             return this.list.isVisible();
42473         },
42474         
42475         collapse : function()
42476         {
42477             if(!this.isExpanded()){
42478                 return;
42479             }
42480             this.list.hide();
42481             Roo.get(document).un('mousedown', this.collapseIf, this);
42482             Roo.get(document).un('mousewheel', this.collapseIf, this);
42483             this.fireEvent('collapse', this);
42484             this.validate();
42485         },
42486         
42487         expand : function()
42488         {
42489             Roo.log('expand');
42490
42491             if(this.isExpanded() || !this.hasFocus){
42492                 return;
42493             }
42494             
42495             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42496             this.list.setWidth(lw);
42497             
42498             this.list.show();
42499             this.restrictHeight();
42500             
42501             Roo.get(document).on('mousedown', this.collapseIf, this);
42502             Roo.get(document).on('mousewheel', this.collapseIf, this);
42503             
42504             this.fireEvent('expand', this);
42505         },
42506         
42507         restrictHeight : function()
42508         {
42509             this.list.alignTo(this.inputEl(), this.listAlign);
42510             this.list.alignTo(this.inputEl(), this.listAlign);
42511         },
42512         
42513         onViewOver : function(e, t)
42514         {
42515             if(this.inKeyMode){
42516                 return;
42517             }
42518             var item = this.view.findItemFromChild(t);
42519             
42520             if(item){
42521                 var index = this.view.indexOf(item);
42522                 this.select(index, false);
42523             }
42524         },
42525
42526         // private
42527         onViewClick : function(view, doFocus, el, e)
42528         {
42529             var index = this.view.getSelectedIndexes()[0];
42530             
42531             var r = this.store.getAt(index);
42532             
42533             if(r){
42534                 this.onSelect(r, index);
42535             }
42536             if(doFocus !== false && !this.blockFocus){
42537                 this.inputEl().focus();
42538             }
42539         },
42540         
42541         onViewMove : function(e, t)
42542         {
42543             this.inKeyMode = false;
42544         },
42545         
42546         select : function(index, scrollIntoView)
42547         {
42548             this.selectedIndex = index;
42549             this.view.select(index);
42550             if(scrollIntoView !== false){
42551                 var el = this.view.getNode(index);
42552                 if(el){
42553                     this.list.scrollChildIntoView(el, false);
42554                 }
42555             }
42556         },
42557         
42558         createList : function()
42559         {
42560             this.list = Roo.get(document.body).createChild({
42561                 tag: 'ul',
42562                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42563                 style: 'display:none'
42564             });
42565             
42566             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42567         },
42568         
42569         collapseIf : function(e)
42570         {
42571             var in_combo  = e.within(this.el);
42572             var in_list =  e.within(this.list);
42573             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42574             
42575             if (in_combo || in_list || is_list) {
42576                 return;
42577             }
42578             this.collapse();
42579         },
42580         
42581         onSelect : function(record, index)
42582         {
42583             if(this.fireEvent('beforeselect', this, record, index) !== false){
42584                 
42585                 this.setFlagClass(record.data.iso2);
42586                 this.setDialCode(record.data.dialCode);
42587                 this.hasFocus = false;
42588                 this.collapse();
42589                 this.fireEvent('select', this, record, index);
42590             }
42591         },
42592         
42593         flagEl : function()
42594         {
42595             var flag = this.el.select('div.flag',true).first();
42596             if(!flag){
42597                 return false;
42598             }
42599             return flag;
42600         },
42601         
42602         dialCodeHolderEl : function()
42603         {
42604             var d = this.el.select('input.dial-code-holder',true).first();
42605             if(!d){
42606                 return false;
42607             }
42608             return d;
42609         },
42610         
42611         setDialCode : function(v)
42612         {
42613             this.dialCodeHolder.dom.value = '+'+v;
42614         },
42615         
42616         setFlagClass : function(n)
42617         {
42618             this.flag.dom.className = 'flag '+n;
42619         },
42620         
42621         getValue : function()
42622         {
42623             var v = this.inputEl().getValue();
42624             if(this.dialCodeHolder) {
42625                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42626             }
42627             return v;
42628         },
42629         
42630         setValue : function(v)
42631         {
42632             var d = this.getDialCode(v);
42633             
42634             //invalid dial code
42635             if(v.length == 0 || !d || d.length == 0) {
42636                 if(this.rendered){
42637                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42638                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42639                 }
42640                 return;
42641             }
42642             
42643             //valid dial code
42644             this.setFlagClass(this.dialCodeMapping[d].iso2);
42645             this.setDialCode(d);
42646             this.inputEl().dom.value = v.replace('+'+d,'');
42647             this.hiddenEl().dom.value = this.getValue();
42648             
42649             this.validate();
42650         },
42651         
42652         getDialCode : function(v)
42653         {
42654             v = v ||  '';
42655             
42656             if (v.length == 0) {
42657                 return this.dialCodeHolder.dom.value;
42658             }
42659             
42660             var dialCode = "";
42661             if (v.charAt(0) != "+") {
42662                 return false;
42663             }
42664             var numericChars = "";
42665             for (var i = 1; i < v.length; i++) {
42666               var c = v.charAt(i);
42667               if (!isNaN(c)) {
42668                 numericChars += c;
42669                 if (this.dialCodeMapping[numericChars]) {
42670                   dialCode = v.substr(1, i);
42671                 }
42672                 if (numericChars.length == 4) {
42673                   break;
42674                 }
42675               }
42676             }
42677             return dialCode;
42678         },
42679         
42680         reset : function()
42681         {
42682             this.setValue(this.defaultDialCode);
42683             this.validate();
42684         },
42685         
42686         hiddenEl : function()
42687         {
42688             return this.el.select('input.hidden-tel-input',true).first();
42689         },
42690         
42691         // after setting val
42692         onKeyUp : function(e){
42693             this.setValue(this.getValue());
42694         },
42695         
42696         onKeyPress : function(e){
42697             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42698                 e.stopEvent();
42699             }
42700         }
42701         
42702 });
42703 /**
42704  * @class Roo.bootstrap.MoneyField
42705  * @extends Roo.bootstrap.ComboBox
42706  * Bootstrap MoneyField class
42707  * 
42708  * @constructor
42709  * Create a new MoneyField.
42710  * @param {Object} config Configuration options
42711  */
42712
42713 Roo.bootstrap.MoneyField = function(config) {
42714     
42715     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42716     
42717 };
42718
42719 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42720     
42721     /**
42722      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42723      */
42724     allowDecimals : true,
42725     /**
42726      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42727      */
42728     decimalSeparator : ".",
42729     /**
42730      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42731      */
42732     decimalPrecision : 0,
42733     /**
42734      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42735      */
42736     allowNegative : true,
42737     /**
42738      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42739      */
42740     allowZero: true,
42741     /**
42742      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42743      */
42744     minValue : Number.NEGATIVE_INFINITY,
42745     /**
42746      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42747      */
42748     maxValue : Number.MAX_VALUE,
42749     /**
42750      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42751      */
42752     minText : "The minimum value for this field is {0}",
42753     /**
42754      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42755      */
42756     maxText : "The maximum value for this field is {0}",
42757     /**
42758      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42759      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42760      */
42761     nanText : "{0} is not a valid number",
42762     /**
42763      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42764      */
42765     castInt : true,
42766     /**
42767      * @cfg {String} defaults currency of the MoneyField
42768      * value should be in lkey
42769      */
42770     defaultCurrency : false,
42771     /**
42772      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42773      */
42774     thousandsDelimiter : false,
42775     /**
42776      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42777      */
42778     max_length: false,
42779     
42780     inputlg : 9,
42781     inputmd : 9,
42782     inputsm : 9,
42783     inputxs : 6,
42784     
42785     store : false,
42786     
42787     getAutoCreate : function()
42788     {
42789         var align = this.labelAlign || this.parentLabelAlign();
42790         
42791         var id = Roo.id();
42792
42793         var cfg = {
42794             cls: 'form-group',
42795             cn: []
42796         };
42797
42798         var input =  {
42799             tag: 'input',
42800             id : id,
42801             cls : 'form-control roo-money-amount-input',
42802             autocomplete: 'new-password'
42803         };
42804         
42805         var hiddenInput = {
42806             tag: 'input',
42807             type: 'hidden',
42808             id: Roo.id(),
42809             cls: 'hidden-number-input'
42810         };
42811         
42812         if(this.max_length) {
42813             input.maxlength = this.max_length; 
42814         }
42815         
42816         if (this.name) {
42817             hiddenInput.name = this.name;
42818         }
42819
42820         if (this.disabled) {
42821             input.disabled = true;
42822         }
42823
42824         var clg = 12 - this.inputlg;
42825         var cmd = 12 - this.inputmd;
42826         var csm = 12 - this.inputsm;
42827         var cxs = 12 - this.inputxs;
42828         
42829         var container = {
42830             tag : 'div',
42831             cls : 'row roo-money-field',
42832             cn : [
42833                 {
42834                     tag : 'div',
42835                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42836                     cn : [
42837                         {
42838                             tag : 'div',
42839                             cls: 'roo-select2-container input-group',
42840                             cn: [
42841                                 {
42842                                     tag : 'input',
42843                                     cls : 'form-control roo-money-currency-input',
42844                                     autocomplete: 'new-password',
42845                                     readOnly : 1,
42846                                     name : this.currencyName
42847                                 },
42848                                 {
42849                                     tag :'span',
42850                                     cls : 'input-group-addon',
42851                                     cn : [
42852                                         {
42853                                             tag: 'span',
42854                                             cls: 'caret'
42855                                         }
42856                                     ]
42857                                 }
42858                             ]
42859                         }
42860                     ]
42861                 },
42862                 {
42863                     tag : 'div',
42864                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42865                     cn : [
42866                         {
42867                             tag: 'div',
42868                             cls: this.hasFeedback ? 'has-feedback' : '',
42869                             cn: [
42870                                 input
42871                             ]
42872                         }
42873                     ]
42874                 }
42875             ]
42876             
42877         };
42878         
42879         if (this.fieldLabel.length) {
42880             var indicator = {
42881                 tag: 'i',
42882                 tooltip: 'This field is required'
42883             };
42884
42885             var label = {
42886                 tag: 'label',
42887                 'for':  id,
42888                 cls: 'control-label',
42889                 cn: []
42890             };
42891
42892             var label_text = {
42893                 tag: 'span',
42894                 html: this.fieldLabel
42895             };
42896
42897             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42898             label.cn = [
42899                 indicator,
42900                 label_text
42901             ];
42902
42903             if(this.indicatorpos == 'right') {
42904                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42905                 label.cn = [
42906                     label_text,
42907                     indicator
42908                 ];
42909             }
42910
42911             if(align == 'left') {
42912                 container = {
42913                     tag: 'div',
42914                     cn: [
42915                         container
42916                     ]
42917                 };
42918
42919                 if(this.labelWidth > 12){
42920                     label.style = "width: " + this.labelWidth + 'px';
42921                 }
42922                 if(this.labelWidth < 13 && this.labelmd == 0){
42923                     this.labelmd = this.labelWidth;
42924                 }
42925                 if(this.labellg > 0){
42926                     label.cls += ' col-lg-' + this.labellg;
42927                     input.cls += ' col-lg-' + (12 - this.labellg);
42928                 }
42929                 if(this.labelmd > 0){
42930                     label.cls += ' col-md-' + this.labelmd;
42931                     container.cls += ' col-md-' + (12 - this.labelmd);
42932                 }
42933                 if(this.labelsm > 0){
42934                     label.cls += ' col-sm-' + this.labelsm;
42935                     container.cls += ' col-sm-' + (12 - this.labelsm);
42936                 }
42937                 if(this.labelxs > 0){
42938                     label.cls += ' col-xs-' + this.labelxs;
42939                     container.cls += ' col-xs-' + (12 - this.labelxs);
42940                 }
42941             }
42942         }
42943
42944         cfg.cn = [
42945             label,
42946             container,
42947             hiddenInput
42948         ];
42949         
42950         var settings = this;
42951
42952         ['xs','sm','md','lg'].map(function(size){
42953             if (settings[size]) {
42954                 cfg.cls += ' col-' + size + '-' + settings[size];
42955             }
42956         });
42957         
42958         return cfg;
42959     },
42960     
42961     initEvents : function()
42962     {
42963         this.indicator = this.indicatorEl();
42964         
42965         this.initCurrencyEvent();
42966         
42967         this.initNumberEvent();
42968     },
42969     
42970     initCurrencyEvent : function()
42971     {
42972         if (!this.store) {
42973             throw "can not find store for combo";
42974         }
42975         
42976         this.store = Roo.factory(this.store, Roo.data);
42977         this.store.parent = this;
42978         
42979         this.createList();
42980         
42981         this.triggerEl = this.el.select('.input-group-addon', true).first();
42982         
42983         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42984         
42985         var _this = this;
42986         
42987         (function(){
42988             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42989             _this.list.setWidth(lw);
42990         }).defer(100);
42991         
42992         this.list.on('mouseover', this.onViewOver, this);
42993         this.list.on('mousemove', this.onViewMove, this);
42994         this.list.on('scroll', this.onViewScroll, this);
42995         
42996         if(!this.tpl){
42997             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42998         }
42999         
43000         this.view = new Roo.View(this.list, this.tpl, {
43001             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43002         });
43003         
43004         this.view.on('click', this.onViewClick, this);
43005         
43006         this.store.on('beforeload', this.onBeforeLoad, this);
43007         this.store.on('load', this.onLoad, this);
43008         this.store.on('loadexception', this.onLoadException, this);
43009         
43010         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43011             "up" : function(e){
43012                 this.inKeyMode = true;
43013                 this.selectPrev();
43014             },
43015
43016             "down" : function(e){
43017                 if(!this.isExpanded()){
43018                     this.onTriggerClick();
43019                 }else{
43020                     this.inKeyMode = true;
43021                     this.selectNext();
43022                 }
43023             },
43024
43025             "enter" : function(e){
43026                 this.collapse();
43027                 
43028                 if(this.fireEvent("specialkey", this, e)){
43029                     this.onViewClick(false);
43030                 }
43031                 
43032                 return true;
43033             },
43034
43035             "esc" : function(e){
43036                 this.collapse();
43037             },
43038
43039             "tab" : function(e){
43040                 this.collapse();
43041                 
43042                 if(this.fireEvent("specialkey", this, e)){
43043                     this.onViewClick(false);
43044                 }
43045                 
43046                 return true;
43047             },
43048
43049             scope : this,
43050
43051             doRelay : function(foo, bar, hname){
43052                 if(hname == 'down' || this.scope.isExpanded()){
43053                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43054                 }
43055                 return true;
43056             },
43057
43058             forceKeyDown: true
43059         });
43060         
43061         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43062         
43063     },
43064     
43065     initNumberEvent : function(e)
43066     {
43067         this.inputEl().on("keydown" , this.fireKey,  this);
43068         this.inputEl().on("focus", this.onFocus,  this);
43069         this.inputEl().on("blur", this.onBlur,  this);
43070         
43071         this.inputEl().relayEvent('keyup', this);
43072         
43073         if(this.indicator){
43074             this.indicator.addClass('invisible');
43075         }
43076  
43077         this.originalValue = this.getValue();
43078         
43079         if(this.validationEvent == 'keyup'){
43080             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43081             this.inputEl().on('keyup', this.filterValidation, this);
43082         }
43083         else if(this.validationEvent !== false){
43084             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43085         }
43086         
43087         if(this.selectOnFocus){
43088             this.on("focus", this.preFocus, this);
43089             
43090         }
43091         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43092             this.inputEl().on("keypress", this.filterKeys, this);
43093         } else {
43094             this.inputEl().relayEvent('keypress', this);
43095         }
43096         
43097         var allowed = "0123456789";
43098         
43099         if(this.allowDecimals){
43100             allowed += this.decimalSeparator;
43101         }
43102         
43103         if(this.allowNegative){
43104             allowed += "-";
43105         }
43106         
43107         if(this.thousandsDelimiter) {
43108             allowed += ",";
43109         }
43110         
43111         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43112         
43113         var keyPress = function(e){
43114             
43115             var k = e.getKey();
43116             
43117             var c = e.getCharCode();
43118             
43119             if(
43120                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43121                     allowed.indexOf(String.fromCharCode(c)) === -1
43122             ){
43123                 e.stopEvent();
43124                 return;
43125             }
43126             
43127             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43128                 return;
43129             }
43130             
43131             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43132                 e.stopEvent();
43133             }
43134         };
43135         
43136         this.inputEl().on("keypress", keyPress, this);
43137         
43138     },
43139     
43140     onTriggerClick : function(e)
43141     {   
43142         if(this.disabled){
43143             return;
43144         }
43145         
43146         this.page = 0;
43147         this.loadNext = false;
43148         
43149         if(this.isExpanded()){
43150             this.collapse();
43151             return;
43152         }
43153         
43154         this.hasFocus = true;
43155         
43156         if(this.triggerAction == 'all') {
43157             this.doQuery(this.allQuery, true);
43158             return;
43159         }
43160         
43161         this.doQuery(this.getRawValue());
43162     },
43163     
43164     getCurrency : function()
43165     {   
43166         var v = this.currencyEl().getValue();
43167         
43168         return v;
43169     },
43170     
43171     restrictHeight : function()
43172     {
43173         this.list.alignTo(this.currencyEl(), this.listAlign);
43174         this.list.alignTo(this.currencyEl(), this.listAlign);
43175     },
43176     
43177     onViewClick : function(view, doFocus, el, e)
43178     {
43179         var index = this.view.getSelectedIndexes()[0];
43180         
43181         var r = this.store.getAt(index);
43182         
43183         if(r){
43184             this.onSelect(r, index);
43185         }
43186     },
43187     
43188     onSelect : function(record, index){
43189         
43190         if(this.fireEvent('beforeselect', this, record, index) !== false){
43191         
43192             this.setFromCurrencyData(index > -1 ? record.data : false);
43193             
43194             this.collapse();
43195             
43196             this.fireEvent('select', this, record, index);
43197         }
43198     },
43199     
43200     setFromCurrencyData : function(o)
43201     {
43202         var currency = '';
43203         
43204         this.lastCurrency = o;
43205         
43206         if (this.currencyField) {
43207             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43208         } else {
43209             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43210         }
43211         
43212         this.lastSelectionText = currency;
43213         
43214         //setting default currency
43215         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43216             this.setCurrency(this.defaultCurrency);
43217             return;
43218         }
43219         
43220         this.setCurrency(currency);
43221     },
43222     
43223     setFromData : function(o)
43224     {
43225         var c = {};
43226         
43227         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43228         
43229         this.setFromCurrencyData(c);
43230         
43231         var value = '';
43232         
43233         if (this.name) {
43234             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43235         } else {
43236             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43237         }
43238         
43239         this.setValue(value);
43240         
43241     },
43242     
43243     setCurrency : function(v)
43244     {   
43245         this.currencyValue = v;
43246         
43247         if(this.rendered){
43248             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43249             this.validate();
43250         }
43251     },
43252     
43253     setValue : function(v)
43254     {
43255         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43256         
43257         this.value = v;
43258         
43259         if(this.rendered){
43260             
43261             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43262             
43263             this.inputEl().dom.value = (v == '') ? '' :
43264                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43265             
43266             if(!this.allowZero && v === '0') {
43267                 this.hiddenEl().dom.value = '';
43268                 this.inputEl().dom.value = '';
43269             }
43270             
43271             this.validate();
43272         }
43273     },
43274     
43275     getRawValue : function()
43276     {
43277         var v = this.inputEl().getValue();
43278         
43279         return v;
43280     },
43281     
43282     getValue : function()
43283     {
43284         return this.fixPrecision(this.parseValue(this.getRawValue()));
43285     },
43286     
43287     parseValue : function(value)
43288     {
43289         if(this.thousandsDelimiter) {
43290             value += "";
43291             r = new RegExp(",", "g");
43292             value = value.replace(r, "");
43293         }
43294         
43295         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43296         return isNaN(value) ? '' : value;
43297         
43298     },
43299     
43300     fixPrecision : function(value)
43301     {
43302         if(this.thousandsDelimiter) {
43303             value += "";
43304             r = new RegExp(",", "g");
43305             value = value.replace(r, "");
43306         }
43307         
43308         var nan = isNaN(value);
43309         
43310         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43311             return nan ? '' : value;
43312         }
43313         return parseFloat(value).toFixed(this.decimalPrecision);
43314     },
43315     
43316     decimalPrecisionFcn : function(v)
43317     {
43318         return Math.floor(v);
43319     },
43320     
43321     validateValue : function(value)
43322     {
43323         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43324             return false;
43325         }
43326         
43327         var num = this.parseValue(value);
43328         
43329         if(isNaN(num)){
43330             this.markInvalid(String.format(this.nanText, value));
43331             return false;
43332         }
43333         
43334         if(num < this.minValue){
43335             this.markInvalid(String.format(this.minText, this.minValue));
43336             return false;
43337         }
43338         
43339         if(num > this.maxValue){
43340             this.markInvalid(String.format(this.maxText, this.maxValue));
43341             return false;
43342         }
43343         
43344         return true;
43345     },
43346     
43347     validate : function()
43348     {
43349         if(this.disabled || this.allowBlank){
43350             this.markValid();
43351             return true;
43352         }
43353         
43354         var currency = this.getCurrency();
43355         
43356         if(this.validateValue(this.getRawValue()) && currency.length){
43357             this.markValid();
43358             return true;
43359         }
43360         
43361         this.markInvalid();
43362         return false;
43363     },
43364     
43365     getName: function()
43366     {
43367         return this.name;
43368     },
43369     
43370     beforeBlur : function()
43371     {
43372         if(!this.castInt){
43373             return;
43374         }
43375         
43376         var v = this.parseValue(this.getRawValue());
43377         
43378         if(v || v == 0){
43379             this.setValue(v);
43380         }
43381     },
43382     
43383     onBlur : function()
43384     {
43385         this.beforeBlur();
43386         
43387         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43388             //this.el.removeClass(this.focusClass);
43389         }
43390         
43391         this.hasFocus = false;
43392         
43393         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43394             this.validate();
43395         }
43396         
43397         var v = this.getValue();
43398         
43399         if(String(v) !== String(this.startValue)){
43400             this.fireEvent('change', this, v, this.startValue);
43401         }
43402         
43403         this.fireEvent("blur", this);
43404     },
43405     
43406     inputEl : function()
43407     {
43408         return this.el.select('.roo-money-amount-input', true).first();
43409     },
43410     
43411     currencyEl : function()
43412     {
43413         return this.el.select('.roo-money-currency-input', true).first();
43414     },
43415     
43416     hiddenEl : function()
43417     {
43418         return this.el.select('input.hidden-number-input',true).first();
43419     }
43420     
43421 });/**
43422  * @class Roo.bootstrap.BezierSignature
43423  * @extends Roo.bootstrap.Component
43424  * Bootstrap BezierSignature class
43425  * This script refer to:
43426  *    Title: Signature Pad
43427  *    Author: szimek
43428  *    Availability: https://github.com/szimek/signature_pad
43429  *
43430  * @constructor
43431  * Create a new BezierSignature
43432  * @param {Object} config The config object
43433  */
43434
43435 Roo.bootstrap.BezierSignature = function(config){
43436     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43437     this.addEvents({
43438         "resize" : true
43439     });
43440 };
43441
43442 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43443 {
43444      
43445     curve_data: [],
43446     
43447     is_empty: true,
43448     
43449     mouse_btn_down: true,
43450     
43451     /**
43452      * @cfg {int} canvas height
43453      */
43454     canvas_height: '200px',
43455     
43456     /**
43457      * @cfg {float|function} Radius of a single dot.
43458      */ 
43459     dot_size: false,
43460     
43461     /**
43462      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43463      */
43464     min_width: 0.5,
43465     
43466     /**
43467      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43468      */
43469     max_width: 2.5,
43470     
43471     /**
43472      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43473      */
43474     throttle: 16,
43475     
43476     /**
43477      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43478      */
43479     min_distance: 5,
43480     
43481     /**
43482      * @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.
43483      */
43484     bg_color: 'rgba(0, 0, 0, 0)',
43485     
43486     /**
43487      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43488      */
43489     dot_color: 'black',
43490     
43491     /**
43492      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43493      */ 
43494     velocity_filter_weight: 0.7,
43495     
43496     /**
43497      * @cfg {function} Callback when stroke begin. 
43498      */
43499     onBegin: false,
43500     
43501     /**
43502      * @cfg {function} Callback when stroke end.
43503      */
43504     onEnd: false,
43505     
43506     getAutoCreate : function()
43507     {
43508         var cls = 'roo-signature column';
43509         
43510         if(this.cls){
43511             cls += ' ' + this.cls;
43512         }
43513         
43514         var col_sizes = [
43515             'lg',
43516             'md',
43517             'sm',
43518             'xs'
43519         ];
43520         
43521         for(var i = 0; i < col_sizes.length; i++) {
43522             if(this[col_sizes[i]]) {
43523                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43524             }
43525         }
43526         
43527         var cfg = {
43528             tag: 'div',
43529             cls: cls,
43530             cn: [
43531                 {
43532                     tag: 'div',
43533                     cls: 'roo-signature-body',
43534                     cn: [
43535                         {
43536                             tag: 'canvas',
43537                             cls: 'roo-signature-body-canvas',
43538                             height: this.canvas_height,
43539                             width: this.canvas_width
43540                         }
43541                     ]
43542                 },
43543                 {
43544                     tag: 'input',
43545                     type: 'file',
43546                     style: 'display: none'
43547                 }
43548             ]
43549         };
43550         
43551         return cfg;
43552     },
43553     
43554     initEvents: function() 
43555     {
43556         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43557         
43558         var canvas = this.canvasEl();
43559         
43560         // mouse && touch event swapping...
43561         canvas.dom.style.touchAction = 'none';
43562         canvas.dom.style.msTouchAction = 'none';
43563         
43564         this.mouse_btn_down = false;
43565         canvas.on('mousedown', this._handleMouseDown, this);
43566         canvas.on('mousemove', this._handleMouseMove, this);
43567         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43568         
43569         if (window.PointerEvent) {
43570             canvas.on('pointerdown', this._handleMouseDown, this);
43571             canvas.on('pointermove', this._handleMouseMove, this);
43572             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43573         }
43574         
43575         if ('ontouchstart' in window) {
43576             canvas.on('touchstart', this._handleTouchStart, this);
43577             canvas.on('touchmove', this._handleTouchMove, this);
43578             canvas.on('touchend', this._handleTouchEnd, this);
43579         }
43580         
43581         Roo.EventManager.onWindowResize(this.resize, this, true);
43582         
43583         // file input event
43584         this.fileEl().on('change', this.uploadImage, this);
43585         
43586         this.clear();
43587         
43588         this.resize();
43589     },
43590     
43591     resize: function(){
43592         
43593         var canvas = this.canvasEl().dom;
43594         var ctx = this.canvasElCtx();
43595         var img_data = false;
43596         
43597         if(canvas.width > 0) {
43598             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43599         }
43600         // setting canvas width will clean img data
43601         canvas.width = 0;
43602         
43603         var style = window.getComputedStyle ? 
43604             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43605             
43606         var padding_left = parseInt(style.paddingLeft) || 0;
43607         var padding_right = parseInt(style.paddingRight) || 0;
43608         
43609         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43610         
43611         if(img_data) {
43612             ctx.putImageData(img_data, 0, 0);
43613         }
43614     },
43615     
43616     _handleMouseDown: function(e)
43617     {
43618         if (e.browserEvent.which === 1) {
43619             this.mouse_btn_down = true;
43620             this.strokeBegin(e);
43621         }
43622     },
43623     
43624     _handleMouseMove: function (e)
43625     {
43626         if (this.mouse_btn_down) {
43627             this.strokeMoveUpdate(e);
43628         }
43629     },
43630     
43631     _handleMouseUp: function (e)
43632     {
43633         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43634             this.mouse_btn_down = false;
43635             this.strokeEnd(e);
43636         }
43637     },
43638     
43639     _handleTouchStart: function (e) {
43640         
43641         e.preventDefault();
43642         if (e.browserEvent.targetTouches.length === 1) {
43643             // var touch = e.browserEvent.changedTouches[0];
43644             // this.strokeBegin(touch);
43645             
43646              this.strokeBegin(e); // assume e catching the correct xy...
43647         }
43648     },
43649     
43650     _handleTouchMove: function (e) {
43651         e.preventDefault();
43652         // var touch = event.targetTouches[0];
43653         // _this._strokeMoveUpdate(touch);
43654         this.strokeMoveUpdate(e);
43655     },
43656     
43657     _handleTouchEnd: function (e) {
43658         var wasCanvasTouched = e.target === this.canvasEl().dom;
43659         if (wasCanvasTouched) {
43660             e.preventDefault();
43661             // var touch = event.changedTouches[0];
43662             // _this._strokeEnd(touch);
43663             this.strokeEnd(e);
43664         }
43665     },
43666     
43667     reset: function () {
43668         this._lastPoints = [];
43669         this._lastVelocity = 0;
43670         this._lastWidth = (this.min_width + this.max_width) / 2;
43671         this.canvasElCtx().fillStyle = this.dot_color;
43672     },
43673     
43674     strokeMoveUpdate: function(e)
43675     {
43676         this.strokeUpdate(e);
43677         
43678         if (this.throttle) {
43679             this.throttleStroke(this.strokeUpdate, this.throttle);
43680         }
43681         else {
43682             this.strokeUpdate(e);
43683         }
43684     },
43685     
43686     strokeBegin: function(e)
43687     {
43688         var newPointGroup = {
43689             color: this.dot_color,
43690             points: []
43691         };
43692         
43693         if (typeof this.onBegin === 'function') {
43694             this.onBegin(e);
43695         }
43696         
43697         this.curve_data.push(newPointGroup);
43698         this.reset();
43699         this.strokeUpdate(e);
43700     },
43701     
43702     strokeUpdate: function(e)
43703     {
43704         var rect = this.canvasEl().dom.getBoundingClientRect();
43705         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43706         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43707         var lastPoints = lastPointGroup.points;
43708         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43709         var isLastPointTooClose = lastPoint
43710             ? point.distanceTo(lastPoint) <= this.min_distance
43711             : false;
43712         var color = lastPointGroup.color;
43713         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43714             var curve = this.addPoint(point);
43715             if (!lastPoint) {
43716                 this.drawDot({color: color, point: point});
43717             }
43718             else if (curve) {
43719                 this.drawCurve({color: color, curve: curve});
43720             }
43721             lastPoints.push({
43722                 time: point.time,
43723                 x: point.x,
43724                 y: point.y
43725             });
43726         }
43727     },
43728     
43729     strokeEnd: function(e)
43730     {
43731         this.strokeUpdate(e);
43732         if (typeof this.onEnd === 'function') {
43733             this.onEnd(e);
43734         }
43735     },
43736     
43737     addPoint:  function (point) {
43738         var _lastPoints = this._lastPoints;
43739         _lastPoints.push(point);
43740         if (_lastPoints.length > 2) {
43741             if (_lastPoints.length === 3) {
43742                 _lastPoints.unshift(_lastPoints[0]);
43743             }
43744             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43745             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43746             _lastPoints.shift();
43747             return curve;
43748         }
43749         return null;
43750     },
43751     
43752     calculateCurveWidths: function (startPoint, endPoint) {
43753         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43754             (1 - this.velocity_filter_weight) * this._lastVelocity;
43755
43756         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43757         var widths = {
43758             end: newWidth,
43759             start: this._lastWidth
43760         };
43761         
43762         this._lastVelocity = velocity;
43763         this._lastWidth = newWidth;
43764         return widths;
43765     },
43766     
43767     drawDot: function (_a) {
43768         var color = _a.color, point = _a.point;
43769         var ctx = this.canvasElCtx();
43770         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43771         ctx.beginPath();
43772         this.drawCurveSegment(point.x, point.y, width);
43773         ctx.closePath();
43774         ctx.fillStyle = color;
43775         ctx.fill();
43776     },
43777     
43778     drawCurve: function (_a) {
43779         var color = _a.color, curve = _a.curve;
43780         var ctx = this.canvasElCtx();
43781         var widthDelta = curve.endWidth - curve.startWidth;
43782         var drawSteps = Math.floor(curve.length()) * 2;
43783         ctx.beginPath();
43784         ctx.fillStyle = color;
43785         for (var i = 0; i < drawSteps; i += 1) {
43786         var t = i / drawSteps;
43787         var tt = t * t;
43788         var ttt = tt * t;
43789         var u = 1 - t;
43790         var uu = u * u;
43791         var uuu = uu * u;
43792         var x = uuu * curve.startPoint.x;
43793         x += 3 * uu * t * curve.control1.x;
43794         x += 3 * u * tt * curve.control2.x;
43795         x += ttt * curve.endPoint.x;
43796         var y = uuu * curve.startPoint.y;
43797         y += 3 * uu * t * curve.control1.y;
43798         y += 3 * u * tt * curve.control2.y;
43799         y += ttt * curve.endPoint.y;
43800         var width = curve.startWidth + ttt * widthDelta;
43801         this.drawCurveSegment(x, y, width);
43802         }
43803         ctx.closePath();
43804         ctx.fill();
43805     },
43806     
43807     drawCurveSegment: function (x, y, width) {
43808         var ctx = this.canvasElCtx();
43809         ctx.moveTo(x, y);
43810         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43811         this.is_empty = false;
43812     },
43813     
43814     clear: function()
43815     {
43816         var ctx = this.canvasElCtx();
43817         var canvas = this.canvasEl().dom;
43818         ctx.fillStyle = this.bg_color;
43819         ctx.clearRect(0, 0, canvas.width, canvas.height);
43820         ctx.fillRect(0, 0, canvas.width, canvas.height);
43821         this.curve_data = [];
43822         this.reset();
43823         this.is_empty = true;
43824     },
43825     
43826     fileEl: function()
43827     {
43828         return  this.el.select('input',true).first();
43829     },
43830     
43831     canvasEl: function()
43832     {
43833         return this.el.select('canvas',true).first();
43834     },
43835     
43836     canvasElCtx: function()
43837     {
43838         return this.el.select('canvas',true).first().dom.getContext('2d');
43839     },
43840     
43841     getImage: function(type)
43842     {
43843         if(this.is_empty) {
43844             return false;
43845         }
43846         
43847         // encryption ?
43848         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43849     },
43850     
43851     drawFromImage: function(img_src)
43852     {
43853         var img = new Image();
43854         
43855         img.onload = function(){
43856             this.canvasElCtx().drawImage(img, 0, 0);
43857         }.bind(this);
43858         
43859         img.src = img_src;
43860         
43861         this.is_empty = false;
43862     },
43863     
43864     selectImage: function()
43865     {
43866         this.fileEl().dom.click();
43867     },
43868     
43869     uploadImage: function(e)
43870     {
43871         var reader = new FileReader();
43872         
43873         reader.onload = function(e){
43874             var img = new Image();
43875             img.onload = function(){
43876                 this.reset();
43877                 this.canvasElCtx().drawImage(img, 0, 0);
43878             }.bind(this);
43879             img.src = e.target.result;
43880         }.bind(this);
43881         
43882         reader.readAsDataURL(e.target.files[0]);
43883     },
43884     
43885     // Bezier Point Constructor
43886     Point: (function () {
43887         function Point(x, y, time) {
43888             this.x = x;
43889             this.y = y;
43890             this.time = time || Date.now();
43891         }
43892         Point.prototype.distanceTo = function (start) {
43893             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43894         };
43895         Point.prototype.equals = function (other) {
43896             return this.x === other.x && this.y === other.y && this.time === other.time;
43897         };
43898         Point.prototype.velocityFrom = function (start) {
43899             return this.time !== start.time
43900             ? this.distanceTo(start) / (this.time - start.time)
43901             : 0;
43902         };
43903         return Point;
43904     }()),
43905     
43906     
43907     // Bezier Constructor
43908     Bezier: (function () {
43909         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43910             this.startPoint = startPoint;
43911             this.control2 = control2;
43912             this.control1 = control1;
43913             this.endPoint = endPoint;
43914             this.startWidth = startWidth;
43915             this.endWidth = endWidth;
43916         }
43917         Bezier.fromPoints = function (points, widths, scope) {
43918             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43919             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43920             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43921         };
43922         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43923             var dx1 = s1.x - s2.x;
43924             var dy1 = s1.y - s2.y;
43925             var dx2 = s2.x - s3.x;
43926             var dy2 = s2.y - s3.y;
43927             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43928             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43929             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43930             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43931             var dxm = m1.x - m2.x;
43932             var dym = m1.y - m2.y;
43933             var k = l2 / (l1 + l2);
43934             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43935             var tx = s2.x - cm.x;
43936             var ty = s2.y - cm.y;
43937             return {
43938                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43939                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43940             };
43941         };
43942         Bezier.prototype.length = function () {
43943             var steps = 10;
43944             var length = 0;
43945             var px;
43946             var py;
43947             for (var i = 0; i <= steps; i += 1) {
43948                 var t = i / steps;
43949                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43950                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43951                 if (i > 0) {
43952                     var xdiff = cx - px;
43953                     var ydiff = cy - py;
43954                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43955                 }
43956                 px = cx;
43957                 py = cy;
43958             }
43959             return length;
43960         };
43961         Bezier.prototype.point = function (t, start, c1, c2, end) {
43962             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43963             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43964             + (3.0 * c2 * (1.0 - t) * t * t)
43965             + (end * t * t * t);
43966         };
43967         return Bezier;
43968     }()),
43969     
43970     throttleStroke: function(fn, wait) {
43971       if (wait === void 0) { wait = 250; }
43972       var previous = 0;
43973       var timeout = null;
43974       var result;
43975       var storedContext;
43976       var storedArgs;
43977       var later = function () {
43978           previous = Date.now();
43979           timeout = null;
43980           result = fn.apply(storedContext, storedArgs);
43981           if (!timeout) {
43982               storedContext = null;
43983               storedArgs = [];
43984           }
43985       };
43986       return function wrapper() {
43987           var args = [];
43988           for (var _i = 0; _i < arguments.length; _i++) {
43989               args[_i] = arguments[_i];
43990           }
43991           var now = Date.now();
43992           var remaining = wait - (now - previous);
43993           storedContext = this;
43994           storedArgs = args;
43995           if (remaining <= 0 || remaining > wait) {
43996               if (timeout) {
43997                   clearTimeout(timeout);
43998                   timeout = null;
43999               }
44000               previous = now;
44001               result = fn.apply(storedContext, storedArgs);
44002               if (!timeout) {
44003                   storedContext = null;
44004                   storedArgs = [];
44005               }
44006           }
44007           else if (!timeout) {
44008               timeout = window.setTimeout(later, remaining);
44009           }
44010           return result;
44011       };
44012   }
44013   
44014 });
44015
44016  
44017
44018