roojs-bootstrap.js
[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['display' + (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         move_card.parent().removeCard(move_card);
2523         
2524         
2525         var dom = move_card.el.dom;
2526         dom.style.width = ''; // clear with - which is set by drag.
2527         
2528         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529             var cardel = next_to_card.el.dom;
2530             
2531             if (position == 'above' ) {
2532                 cardel.parentNode.insertBefore(dom, cardel);
2533             } else if (cardel.nextSibling) {
2534                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535             } else {
2536                 cardel.parentNode.append(dom);
2537             }
2538         } else {
2539             // card container???
2540             this.containerEl.dom.append(dom);
2541         }
2542         
2543         //FIXME HANDLE card = true 
2544         
2545         // add this to the correct place in items.
2546         
2547         // remove Card from items.
2548         
2549        
2550         if (this.items.length) {
2551             var nitems = [];
2552             //Roo.log([info.items_n, info.position, this.items.length]);
2553             for (var i =0; i < this.items.length; i++) {
2554                 if (i == to_items_n && position == 'above') {
2555                     nitems.push(move_card);
2556                 }
2557                 nitems.push(this.items[i]);
2558                 if (i == to_items_n && position == 'below') {
2559                     nitems.push(move_card);
2560                 }
2561             }
2562             this.items = nitems;
2563             Roo.log(this.items);
2564         } else {
2565             this.items.push(move_card);
2566         }
2567         
2568         move_card.parentId = this.id;
2569         
2570         return true;
2571         
2572         
2573     },
2574     removeCard : function(c)
2575     {
2576         this.items = this.items.filter(function(e) { return e != c });
2577  
2578         var dom = c.el.dom;
2579         dom.parentNode.removeChild(dom);
2580         dom.style.width = ''; // clear with - which is set by drag.
2581         c.parentId = false;
2582         
2583     },
2584     
2585     /**    Decide whether to drop above or below a View node. */
2586     getDropPoint : function(e, n, dd)
2587     {
2588         if (dd) {
2589              return false;
2590         }
2591         if (n == this.containerEl.dom) {
2592             return "above";
2593         }
2594         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595         var c = t + (b - t) / 2;
2596         var y = Roo.lib.Event.getPageY(e);
2597         if(y <= c) {
2598             return "above";
2599         }else{
2600             return "below";
2601         }
2602     },
2603     onToggleCollapse : function(e)
2604         {
2605         if (this.collapsed) {
2606             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607             this.collapsableEl.addClass('show');
2608             this.collapsed = false;
2609             return;
2610         }
2611         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612         this.collapsableEl.removeClass('show');
2613         this.collapsed = true;
2614         
2615     
2616     },
2617     
2618     onToggleRotate : function(e)
2619     {
2620         this.collapsableEl.removeClass('show');
2621         this.footerEl.removeClass('d-none');
2622         this.el.removeClass('roo-card-rotated');
2623         this.el.removeClass('d-none');
2624         if (this.rotated) {
2625             
2626             this.collapsableEl.addClass('show');
2627             this.rotated = false;
2628             this.fireEvent('rotate', this, this.rotated);
2629             return;
2630         }
2631         this.el.addClass('roo-card-rotated');
2632         this.footerEl.addClass('d-none');
2633         this.el.select('.roo-collapsable').removeClass('show');
2634         
2635         this.rotated = true;
2636         this.fireEvent('rotate', this, this.rotated);
2637     
2638     },
2639     
2640     dropPlaceHolder: function (action, info, data)
2641     {
2642         if (this.dropEl === false) {
2643             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2644             cls : 'd-none'
2645             },true);
2646         }
2647         this.dropEl.removeClass(['d-none', 'd-block']);        
2648         if (action == 'hide') {
2649             
2650             this.dropEl.addClass('d-none');
2651             return;
2652         }
2653         // FIXME - info.card == true!!!
2654         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2655         
2656         if (info.card !== true) {
2657             var cardel = info.card.el.dom;
2658             
2659             if (info.position == 'above') {
2660                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661             } else if (cardel.nextSibling) {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2663             } else {
2664                 cardel.parentNode.append(this.dropEl.dom);
2665             }
2666         } else {
2667             // card container???
2668             this.containerEl.dom.append(this.dropEl.dom);
2669         }
2670         
2671         this.dropEl.addClass('d-block roo-card-dropzone');
2672         
2673         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674         
2675         
2676     
2677     
2678     
2679     },
2680     setHeaderText: function(html)
2681     {
2682         this.headerContainerEl.dom.innerHTML = html;
2683     }
2684
2685     
2686 });
2687
2688 /*
2689  * - LGPL
2690  *
2691  * Card header - holder for the card header elements.
2692  * 
2693  */
2694
2695 /**
2696  * @class Roo.bootstrap.CardHeader
2697  * @extends Roo.bootstrap.Element
2698  * Bootstrap CardHeader class
2699  * @constructor
2700  * Create a new Card Header - that you can embed children into
2701  * @param {Object} config The config object
2702  */
2703
2704 Roo.bootstrap.CardHeader = function(config){
2705     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2706 };
2707
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2709     
2710     
2711     container_method : 'getCardHeader' 
2712     
2713      
2714     
2715     
2716    
2717 });
2718
2719  
2720
2721  /*
2722  * - LGPL
2723  *
2724  * Card footer - holder for the card footer elements.
2725  * 
2726  */
2727
2728 /**
2729  * @class Roo.bootstrap.CardFooter
2730  * @extends Roo.bootstrap.Element
2731  * Bootstrap CardFooter class
2732  * @constructor
2733  * Create a new Card Footer - that you can embed children into
2734  * @param {Object} config The config object
2735  */
2736
2737 Roo.bootstrap.CardFooter = function(config){
2738     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2739 };
2740
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2742     
2743     
2744     container_method : 'getCardFooter' 
2745     
2746      
2747     
2748     
2749    
2750 });
2751
2752  
2753
2754  /*
2755  * - LGPL
2756  *
2757  * Card header - holder for the card header elements.
2758  * 
2759  */
2760
2761 /**
2762  * @class Roo.bootstrap.CardImageTop
2763  * @extends Roo.bootstrap.Element
2764  * Bootstrap CardImageTop class
2765  * @constructor
2766  * Create a new Card Image Top container
2767  * @param {Object} config The config object
2768  */
2769
2770 Roo.bootstrap.CardImageTop = function(config){
2771     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2772 };
2773
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2775     
2776    
2777     container_method : 'getCardImageTop' 
2778     
2779      
2780     
2781    
2782 });
2783
2784  
2785
2786  /*
2787  * - LGPL
2788  *
2789  * image
2790  * 
2791  */
2792
2793
2794 /**
2795  * @class Roo.bootstrap.Img
2796  * @extends Roo.bootstrap.Component
2797  * Bootstrap Img class
2798  * @cfg {Boolean} imgResponsive false | true
2799  * @cfg {String} border rounded | circle | thumbnail
2800  * @cfg {String} src image source
2801  * @cfg {String} alt image alternative text
2802  * @cfg {String} href a tag href
2803  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804  * @cfg {String} xsUrl xs image source
2805  * @cfg {String} smUrl sm image source
2806  * @cfg {String} mdUrl md image source
2807  * @cfg {String} lgUrl lg image source
2808  * 
2809  * @constructor
2810  * Create a new Input
2811  * @param {Object} config The config object
2812  */
2813
2814 Roo.bootstrap.Img = function(config){
2815     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2816     
2817     this.addEvents({
2818         // img events
2819         /**
2820          * @event click
2821          * The img click event for the img.
2822          * @param {Roo.EventObject} e
2823          */
2824         "click" : true
2825     });
2826 };
2827
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2829     
2830     imgResponsive: true,
2831     border: '',
2832     src: 'about:blank',
2833     href: false,
2834     target: false,
2835     xsUrl: '',
2836     smUrl: '',
2837     mdUrl: '',
2838     lgUrl: '',
2839
2840     getAutoCreate : function()
2841     {   
2842         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843             return this.createSingleImg();
2844         }
2845         
2846         var cfg = {
2847             tag: 'div',
2848             cls: 'roo-image-responsive-group',
2849             cn: []
2850         };
2851         var _this = this;
2852         
2853         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2854             
2855             if(!_this[size + 'Url']){
2856                 return;
2857             }
2858             
2859             var img = {
2860                 tag: 'img',
2861                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862                 html: _this.html || cfg.html,
2863                 src: _this[size + 'Url']
2864             };
2865             
2866             img.cls += ' roo-image-responsive-' + size;
2867             
2868             var s = ['xs', 'sm', 'md', 'lg'];
2869             
2870             s.splice(s.indexOf(size), 1);
2871             
2872             Roo.each(s, function(ss){
2873                 img.cls += ' hidden-' + ss;
2874             });
2875             
2876             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877                 cfg.cls += ' img-' + _this.border;
2878             }
2879             
2880             if(_this.alt){
2881                 cfg.alt = _this.alt;
2882             }
2883             
2884             if(_this.href){
2885                 var a = {
2886                     tag: 'a',
2887                     href: _this.href,
2888                     cn: [
2889                         img
2890                     ]
2891                 };
2892
2893                 if(this.target){
2894                     a.target = _this.target;
2895                 }
2896             }
2897             
2898             cfg.cn.push((_this.href) ? a : img);
2899             
2900         });
2901         
2902         return cfg;
2903     },
2904     
2905     createSingleImg : function()
2906     {
2907         var cfg = {
2908             tag: 'img',
2909             cls: (this.imgResponsive) ? 'img-responsive' : '',
2910             html : null,
2911             src : 'about:blank'  // just incase src get's set to undefined?!?
2912         };
2913         
2914         cfg.html = this.html || cfg.html;
2915         
2916         cfg.src = this.src || cfg.src;
2917         
2918         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919             cfg.cls += ' img-' + this.border;
2920         }
2921         
2922         if(this.alt){
2923             cfg.alt = this.alt;
2924         }
2925         
2926         if(this.href){
2927             var a = {
2928                 tag: 'a',
2929                 href: this.href,
2930                 cn: [
2931                     cfg
2932                 ]
2933             };
2934             
2935             if(this.target){
2936                 a.target = this.target;
2937             }
2938             
2939         }
2940         
2941         return (this.href) ? a : cfg;
2942     },
2943     
2944     initEvents: function() 
2945     {
2946         if(!this.href){
2947             this.el.on('click', this.onClick, this);
2948         }
2949         
2950     },
2951     
2952     onClick : function(e)
2953     {
2954         Roo.log('img onclick');
2955         this.fireEvent('click', this, e);
2956     },
2957     /**
2958      * Sets the url of the image - used to update it
2959      * @param {String} url the url of the image
2960      */
2961     
2962     setSrc : function(url)
2963     {
2964         this.src =  url;
2965         
2966         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967             this.el.dom.src =  url;
2968             return;
2969         }
2970         
2971         this.el.select('img', true).first().dom.src =  url;
2972     }
2973     
2974     
2975    
2976 });
2977
2978  /*
2979  * - LGPL
2980  *
2981  * image
2982  * 
2983  */
2984
2985
2986 /**
2987  * @class Roo.bootstrap.Link
2988  * @extends Roo.bootstrap.Component
2989  * Bootstrap Link Class
2990  * @cfg {String} alt image alternative text
2991  * @cfg {String} href a tag href
2992  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993  * @cfg {String} html the content of the link.
2994  * @cfg {String} anchor name for the anchor link
2995  * @cfg {String} fa - favicon
2996
2997  * @cfg {Boolean} preventDefault (true | false) default false
2998
2999  * 
3000  * @constructor
3001  * Create a new Input
3002  * @param {Object} config The config object
3003  */
3004
3005 Roo.bootstrap.Link = function(config){
3006     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3007     
3008     this.addEvents({
3009         // img events
3010         /**
3011          * @event click
3012          * The img click event for the img.
3013          * @param {Roo.EventObject} e
3014          */
3015         "click" : true
3016     });
3017 };
3018
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3020     
3021     href: false,
3022     target: false,
3023     preventDefault: false,
3024     anchor : false,
3025     alt : false,
3026     fa: false,
3027
3028
3029     getAutoCreate : function()
3030     {
3031         var html = this.html || '';
3032         
3033         if (this.fa !== false) {
3034             html = '<i class="fa fa-' + this.fa + '"></i>';
3035         }
3036         var cfg = {
3037             tag: 'a'
3038         };
3039         // anchor's do not require html/href...
3040         if (this.anchor === false) {
3041             cfg.html = html;
3042             cfg.href = this.href || '#';
3043         } else {
3044             cfg.name = this.anchor;
3045             if (this.html !== false || this.fa !== false) {
3046                 cfg.html = html;
3047             }
3048             if (this.href !== false) {
3049                 cfg.href = this.href;
3050             }
3051         }
3052         
3053         if(this.alt !== false){
3054             cfg.alt = this.alt;
3055         }
3056         
3057         
3058         if(this.target !== false) {
3059             cfg.target = this.target;
3060         }
3061         
3062         return cfg;
3063     },
3064     
3065     initEvents: function() {
3066         
3067         if(!this.href || this.preventDefault){
3068             this.el.on('click', this.onClick, this);
3069         }
3070     },
3071     
3072     onClick : function(e)
3073     {
3074         if(this.preventDefault){
3075             e.preventDefault();
3076         }
3077         //Roo.log('img onclick');
3078         this.fireEvent('click', this, e);
3079     }
3080    
3081 });
3082
3083  /*
3084  * - LGPL
3085  *
3086  * header
3087  * 
3088  */
3089
3090 /**
3091  * @class Roo.bootstrap.Header
3092  * @extends Roo.bootstrap.Component
3093  * Bootstrap Header class
3094  * @cfg {String} html content of header
3095  * @cfg {Number} level (1|2|3|4|5|6) default 1
3096  * 
3097  * @constructor
3098  * Create a new Header
3099  * @param {Object} config The config object
3100  */
3101
3102
3103 Roo.bootstrap.Header  = function(config){
3104     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3105 };
3106
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3108     
3109     //href : false,
3110     html : false,
3111     level : 1,
3112     
3113     
3114     
3115     getAutoCreate : function(){
3116         
3117         
3118         
3119         var cfg = {
3120             tag: 'h' + (1 *this.level),
3121             html: this.html || ''
3122         } ;
3123         
3124         return cfg;
3125     }
3126    
3127 });
3128
3129  
3130
3131  /*
3132  * Based on:
3133  * Ext JS Library 1.1.1
3134  * Copyright(c) 2006-2007, Ext JS, LLC.
3135  *
3136  * Originally Released Under LGPL - original licence link has changed is not relivant.
3137  *
3138  * Fork - LGPL
3139  * <script type="text/javascript">
3140  */
3141  
3142 /**
3143  * @class Roo.bootstrap.MenuMgr
3144  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3145  * @singleton
3146  */
3147 Roo.bootstrap.MenuMgr = function(){
3148    var menus, active, groups = {}, attached = false, lastShow = new Date();
3149
3150    // private - called when first menu is created
3151    function init(){
3152        menus = {};
3153        active = new Roo.util.MixedCollection();
3154        Roo.get(document).addKeyListener(27, function(){
3155            if(active.length > 0){
3156                hideAll();
3157            }
3158        });
3159    }
3160
3161    // private
3162    function hideAll(){
3163        if(active && active.length > 0){
3164            var c = active.clone();
3165            c.each(function(m){
3166                m.hide();
3167            });
3168        }
3169    }
3170
3171    // private
3172    function onHide(m){
3173        active.remove(m);
3174        if(active.length < 1){
3175            Roo.get(document).un("mouseup", onMouseDown);
3176             
3177            attached = false;
3178        }
3179    }
3180
3181    // private
3182    function onShow(m){
3183        var last = active.last();
3184        lastShow = new Date();
3185        active.add(m);
3186        if(!attached){
3187           Roo.get(document).on("mouseup", onMouseDown);
3188            
3189            attached = true;
3190        }
3191        if(m.parentMenu){
3192           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193           m.parentMenu.activeChild = m;
3194        }else if(last && last.isVisible()){
3195           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3196        }
3197    }
3198
3199    // private
3200    function onBeforeHide(m){
3201        if(m.activeChild){
3202            m.activeChild.hide();
3203        }
3204        if(m.autoHideTimer){
3205            clearTimeout(m.autoHideTimer);
3206            delete m.autoHideTimer;
3207        }
3208    }
3209
3210    // private
3211    function onBeforeShow(m){
3212        var pm = m.parentMenu;
3213        if(!pm && !m.allowOtherMenus){
3214            hideAll();
3215        }else if(pm && pm.activeChild && active != m){
3216            pm.activeChild.hide();
3217        }
3218    }
3219
3220    // private this should really trigger on mouseup..
3221    function onMouseDown(e){
3222         Roo.log("on Mouse Up");
3223         
3224         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225             Roo.log("MenuManager hideAll");
3226             hideAll();
3227             e.stopEvent();
3228         }
3229         
3230         
3231    }
3232
3233    // private
3234    function onBeforeCheck(mi, state){
3235        if(state){
3236            var g = groups[mi.group];
3237            for(var i = 0, l = g.length; i < l; i++){
3238                if(g[i] != mi){
3239                    g[i].setChecked(false);
3240                }
3241            }
3242        }
3243    }
3244
3245    return {
3246
3247        /**
3248         * Hides all menus that are currently visible
3249         */
3250        hideAll : function(){
3251             hideAll();  
3252        },
3253
3254        // private
3255        register : function(menu){
3256            if(!menus){
3257                init();
3258            }
3259            menus[menu.id] = menu;
3260            menu.on("beforehide", onBeforeHide);
3261            menu.on("hide", onHide);
3262            menu.on("beforeshow", onBeforeShow);
3263            menu.on("show", onShow);
3264            var g = menu.group;
3265            if(g && menu.events["checkchange"]){
3266                if(!groups[g]){
3267                    groups[g] = [];
3268                }
3269                groups[g].push(menu);
3270                menu.on("checkchange", onCheck);
3271            }
3272        },
3273
3274         /**
3275          * Returns a {@link Roo.menu.Menu} object
3276          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277          * be used to generate and return a new Menu instance.
3278          */
3279        get : function(menu){
3280            if(typeof menu == "string"){ // menu id
3281                return menus[menu];
3282            }else if(menu.events){  // menu instance
3283                return menu;
3284            }
3285            /*else if(typeof menu.length == 'number'){ // array of menu items?
3286                return new Roo.bootstrap.Menu({items:menu});
3287            }else{ // otherwise, must be a config
3288                return new Roo.bootstrap.Menu(menu);
3289            }
3290            */
3291            return false;
3292        },
3293
3294        // private
3295        unregister : function(menu){
3296            delete menus[menu.id];
3297            menu.un("beforehide", onBeforeHide);
3298            menu.un("hide", onHide);
3299            menu.un("beforeshow", onBeforeShow);
3300            menu.un("show", onShow);
3301            var g = menu.group;
3302            if(g && menu.events["checkchange"]){
3303                groups[g].remove(menu);
3304                menu.un("checkchange", onCheck);
3305            }
3306        },
3307
3308        // private
3309        registerCheckable : function(menuItem){
3310            var g = menuItem.group;
3311            if(g){
3312                if(!groups[g]){
3313                    groups[g] = [];
3314                }
3315                groups[g].push(menuItem);
3316                menuItem.on("beforecheckchange", onBeforeCheck);
3317            }
3318        },
3319
3320        // private
3321        unregisterCheckable : function(menuItem){
3322            var g = menuItem.group;
3323            if(g){
3324                groups[g].remove(menuItem);
3325                menuItem.un("beforecheckchange", onBeforeCheck);
3326            }
3327        }
3328    };
3329 }();/*
3330  * - LGPL
3331  *
3332  * menu
3333  * 
3334  */
3335
3336 /**
3337  * @class Roo.bootstrap.Menu
3338  * @extends Roo.bootstrap.Component
3339  * Bootstrap Menu class - container for MenuItems
3340  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3342  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3343  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3344  * 
3345  * @constructor
3346  * Create a new Menu
3347  * @param {Object} config The config object
3348  */
3349
3350
3351 Roo.bootstrap.Menu = function(config){
3352     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353     if (this.registerMenu && this.type != 'treeview')  {
3354         Roo.bootstrap.MenuMgr.register(this);
3355     }
3356     
3357     
3358     this.addEvents({
3359         /**
3360          * @event beforeshow
3361          * Fires before this menu is displayed (return false to block)
3362          * @param {Roo.menu.Menu} this
3363          */
3364         beforeshow : true,
3365         /**
3366          * @event beforehide
3367          * Fires before this menu is hidden (return false to block)
3368          * @param {Roo.menu.Menu} this
3369          */
3370         beforehide : true,
3371         /**
3372          * @event show
3373          * Fires after this menu is displayed
3374          * @param {Roo.menu.Menu} this
3375          */
3376         show : true,
3377         /**
3378          * @event hide
3379          * Fires after this menu is hidden
3380          * @param {Roo.menu.Menu} this
3381          */
3382         hide : true,
3383         /**
3384          * @event click
3385          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386          * @param {Roo.menu.Menu} this
3387          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388          * @param {Roo.EventObject} e
3389          */
3390         click : true,
3391         /**
3392          * @event mouseover
3393          * Fires when the mouse is hovering over this menu
3394          * @param {Roo.menu.Menu} this
3395          * @param {Roo.EventObject} e
3396          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3397          */
3398         mouseover : true,
3399         /**
3400          * @event mouseout
3401          * Fires when the mouse exits this menu
3402          * @param {Roo.menu.Menu} this
3403          * @param {Roo.EventObject} e
3404          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3405          */
3406         mouseout : true,
3407         /**
3408          * @event itemclick
3409          * Fires when a menu item contained in this menu is clicked
3410          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411          * @param {Roo.EventObject} e
3412          */
3413         itemclick: true
3414     });
3415     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3416 };
3417
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3419     
3420    /// html : false,
3421     //align : '',
3422     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3423     type: false,
3424     /**
3425      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3426      */
3427     registerMenu : true,
3428     
3429     menuItems :false, // stores the menu items..
3430     
3431     hidden:true,
3432         
3433     parentMenu : false,
3434     
3435     stopEvent : true,
3436     
3437     isLink : false,
3438     
3439     getChildContainer : function() {
3440         return this.el;  
3441     },
3442     
3443     getAutoCreate : function(){
3444          
3445         //if (['right'].indexOf(this.align)!==-1) {
3446         //    cfg.cn[1].cls += ' pull-right'
3447         //}
3448         
3449         
3450         var cfg = {
3451             tag : 'ul',
3452             cls : 'dropdown-menu' ,
3453             style : 'z-index:1000'
3454             
3455         };
3456         
3457         if (this.type === 'submenu') {
3458             cfg.cls = 'submenu active';
3459         }
3460         if (this.type === 'treeview') {
3461             cfg.cls = 'treeview-menu';
3462         }
3463         
3464         return cfg;
3465     },
3466     initEvents : function() {
3467         
3468        // Roo.log("ADD event");
3469        // Roo.log(this.triggerEl.dom);
3470         
3471         this.triggerEl.on('click', this.onTriggerClick, this);
3472         
3473         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3474         
3475         
3476         if (this.triggerEl.hasClass('nav-item')) {
3477             // dropdown toggle on the 'a' in BS4?
3478             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3479         } else {
3480             this.triggerEl.addClass('dropdown-toggle');
3481         }
3482         if (Roo.isTouch) {
3483             this.el.on('touchstart'  , this.onTouch, this);
3484         }
3485         this.el.on('click' , this.onClick, this);
3486
3487         this.el.on("mouseover", this.onMouseOver, this);
3488         this.el.on("mouseout", this.onMouseOut, this);
3489         
3490     },
3491     
3492     findTargetItem : function(e)
3493     {
3494         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3495         if(!t){
3496             return false;
3497         }
3498         //Roo.log(t);         Roo.log(t.id);
3499         if(t && t.id){
3500             //Roo.log(this.menuitems);
3501             return this.menuitems.get(t.id);
3502             
3503             //return this.items.get(t.menuItemId);
3504         }
3505         
3506         return false;
3507     },
3508     
3509     onTouch : function(e) 
3510     {
3511         Roo.log("menu.onTouch");
3512         //e.stopEvent(); this make the user popdown broken
3513         this.onClick(e);
3514     },
3515     
3516     onClick : function(e)
3517     {
3518         Roo.log("menu.onClick");
3519         
3520         var t = this.findTargetItem(e);
3521         if(!t || t.isContainer){
3522             return;
3523         }
3524         Roo.log(e);
3525         /*
3526         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3527             if(t == this.activeItem && t.shouldDeactivate(e)){
3528                 this.activeItem.deactivate();
3529                 delete this.activeItem;
3530                 return;
3531             }
3532             if(t.canActivate){
3533                 this.setActiveItem(t, true);
3534             }
3535             return;
3536             
3537             
3538         }
3539         */
3540        
3541         Roo.log('pass click event');
3542         
3543         t.onClick(e);
3544         
3545         this.fireEvent("click", this, t, e);
3546         
3547         var _this = this;
3548         
3549         if(!t.href.length || t.href == '#'){
3550             (function() { _this.hide(); }).defer(100);
3551         }
3552         
3553     },
3554     
3555     onMouseOver : function(e){
3556         var t  = this.findTargetItem(e);
3557         //Roo.log(t);
3558         //if(t){
3559         //    if(t.canActivate && !t.disabled){
3560         //        this.setActiveItem(t, true);
3561         //    }
3562         //}
3563         
3564         this.fireEvent("mouseover", this, e, t);
3565     },
3566     isVisible : function(){
3567         return !this.hidden;
3568     },
3569     onMouseOut : function(e){
3570         var t  = this.findTargetItem(e);
3571         
3572         //if(t ){
3573         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3574         //        this.activeItem.deactivate();
3575         //        delete this.activeItem;
3576         //    }
3577         //}
3578         this.fireEvent("mouseout", this, e, t);
3579     },
3580     
3581     
3582     /**
3583      * Displays this menu relative to another element
3584      * @param {String/HTMLElement/Roo.Element} element The element to align to
3585      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586      * the element (defaults to this.defaultAlign)
3587      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3588      */
3589     show : function(el, pos, parentMenu)
3590     {
3591         if (false === this.fireEvent("beforeshow", this)) {
3592             Roo.log("show canceled");
3593             return;
3594         }
3595         this.parentMenu = parentMenu;
3596         if(!this.el){
3597             this.render();
3598         }
3599         
3600         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3601     },
3602      /**
3603      * Displays this menu at a specific xy position
3604      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3606      */
3607     showAt : function(xy, parentMenu, /* private: */_e){
3608         this.parentMenu = parentMenu;
3609         if(!this.el){
3610             this.render();
3611         }
3612         if(_e !== false){
3613             this.fireEvent("beforeshow", this);
3614             //xy = this.el.adjustForConstraints(xy);
3615         }
3616         
3617         //this.el.show();
3618         this.hideMenuItems();
3619         this.hidden = false;
3620         this.triggerEl.addClass('open');
3621         this.el.addClass('show');
3622         
3623         // reassign x when hitting right
3624         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3626         }
3627         
3628         // reassign y when hitting bottom
3629         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3631         }
3632         
3633         // but the list may align on trigger left or trigger top... should it be a properity?
3634         
3635         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3636             this.el.setXY(xy);
3637         }
3638         
3639         this.focus();
3640         this.fireEvent("show", this);
3641     },
3642     
3643     focus : function(){
3644         return;
3645         if(!this.hidden){
3646             this.doFocus.defer(50, this);
3647         }
3648     },
3649
3650     doFocus : function(){
3651         if(!this.hidden){
3652             this.focusEl.focus();
3653         }
3654     },
3655
3656     /**
3657      * Hides this menu and optionally all parent menus
3658      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3659      */
3660     hide : function(deep)
3661     {
3662         if (false === this.fireEvent("beforehide", this)) {
3663             Roo.log("hide canceled");
3664             return;
3665         }
3666         this.hideMenuItems();
3667         if(this.el && this.isVisible()){
3668            
3669             if(this.activeItem){
3670                 this.activeItem.deactivate();
3671                 this.activeItem = null;
3672             }
3673             this.triggerEl.removeClass('open');;
3674             this.el.removeClass('show');
3675             this.hidden = true;
3676             this.fireEvent("hide", this);
3677         }
3678         if(deep === true && this.parentMenu){
3679             this.parentMenu.hide(true);
3680         }
3681     },
3682     
3683     onTriggerClick : function(e)
3684     {
3685         Roo.log('trigger click');
3686         
3687         var target = e.getTarget();
3688         
3689         Roo.log(target.nodeName.toLowerCase());
3690         
3691         if(target.nodeName.toLowerCase() === 'i'){
3692             e.preventDefault();
3693         }
3694         
3695     },
3696     
3697     onTriggerPress  : function(e)
3698     {
3699         Roo.log('trigger press');
3700         //Roo.log(e.getTarget());
3701        // Roo.log(this.triggerEl.dom);
3702        
3703         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704         var pel = Roo.get(e.getTarget());
3705         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706             Roo.log('is treeview or dropdown?');
3707             return;
3708         }
3709         
3710         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3711             return;
3712         }
3713         
3714         if (this.isVisible()) {
3715             Roo.log('hide');
3716             this.hide();
3717         } else {
3718             Roo.log('show');
3719             this.show(this.triggerEl, '?', false);
3720         }
3721         
3722         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3723             e.stopEvent();
3724         }
3725         
3726     },
3727        
3728     
3729     hideMenuItems : function()
3730     {
3731         Roo.log("hide Menu Items");
3732         if (!this.el) { 
3733             return;
3734         }
3735         
3736         this.el.select('.open',true).each(function(aa) {
3737             
3738             aa.removeClass('open');
3739          
3740         });
3741     },
3742     addxtypeChild : function (tree, cntr) {
3743         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3744           
3745         this.menuitems.add(comp);
3746         return comp;
3747
3748     },
3749     getEl : function()
3750     {
3751         Roo.log(this.el);
3752         return this.el;
3753     },
3754     
3755     clear : function()
3756     {
3757         this.getEl().dom.innerHTML = '';
3758         this.menuitems.clear();
3759     }
3760 });
3761
3762  
3763  /*
3764  * - LGPL
3765  *
3766  * menu item
3767  * 
3768  */
3769
3770
3771 /**
3772  * @class Roo.bootstrap.MenuItem
3773  * @extends Roo.bootstrap.Component
3774  * Bootstrap MenuItem class
3775  * @cfg {String} html the menu label
3776  * @cfg {String} href the link
3777  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3780  * @cfg {String} fa favicon to show on left of menu item.
3781  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3782  * 
3783  * 
3784  * @constructor
3785  * Create a new MenuItem
3786  * @param {Object} config The config object
3787  */
3788
3789
3790 Roo.bootstrap.MenuItem = function(config){
3791     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3792     this.addEvents({
3793         // raw events
3794         /**
3795          * @event click
3796          * The raw click event for the entire grid.
3797          * @param {Roo.bootstrap.MenuItem} this
3798          * @param {Roo.EventObject} e
3799          */
3800         "click" : true
3801     });
3802 };
3803
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3805     
3806     href : false,
3807     html : false,
3808     preventDefault: false,
3809     isContainer : false,
3810     active : false,
3811     fa: false,
3812     
3813     getAutoCreate : function(){
3814         
3815         if(this.isContainer){
3816             return {
3817                 tag: 'li',
3818                 cls: 'dropdown-menu-item '
3819             };
3820         }
3821         var ctag = {
3822             tag: 'span',
3823             html: 'Link'
3824         };
3825         
3826         var anc = {
3827             tag : 'a',
3828             cls : 'dropdown-item',
3829             href : '#',
3830             cn : [  ]
3831         };
3832         
3833         if (this.fa !== false) {
3834             anc.cn.push({
3835                 tag : 'i',
3836                 cls : 'fa fa-' + this.fa
3837             });
3838         }
3839         
3840         anc.cn.push(ctag);
3841         
3842         
3843         var cfg= {
3844             tag: 'li',
3845             cls: 'dropdown-menu-item',
3846             cn: [ anc ]
3847         };
3848         if (this.parent().type == 'treeview') {
3849             cfg.cls = 'treeview-menu';
3850         }
3851         if (this.active) {
3852             cfg.cls += ' active';
3853         }
3854         
3855         
3856         
3857         anc.href = this.href || cfg.cn[0].href ;
3858         ctag.html = this.html || cfg.cn[0].html ;
3859         return cfg;
3860     },
3861     
3862     initEvents: function()
3863     {
3864         if (this.parent().type == 'treeview') {
3865             this.el.select('a').on('click', this.onClick, this);
3866         }
3867         
3868         if (this.menu) {
3869             this.menu.parentType = this.xtype;
3870             this.menu.triggerEl = this.el;
3871             this.menu = this.addxtype(Roo.apply({}, this.menu));
3872         }
3873         
3874     },
3875     onClick : function(e)
3876     {
3877         Roo.log('item on click ');
3878         
3879         if(this.preventDefault){
3880             e.preventDefault();
3881         }
3882         //this.parent().hideMenuItems();
3883         
3884         this.fireEvent('click', this, e);
3885     },
3886     getEl : function()
3887     {
3888         return this.el;
3889     } 
3890 });
3891
3892  
3893
3894  /*
3895  * - LGPL
3896  *
3897  * menu separator
3898  * 
3899  */
3900
3901
3902 /**
3903  * @class Roo.bootstrap.MenuSeparator
3904  * @extends Roo.bootstrap.Component
3905  * Bootstrap MenuSeparator class
3906  * 
3907  * @constructor
3908  * Create a new MenuItem
3909  * @param {Object} config The config object
3910  */
3911
3912
3913 Roo.bootstrap.MenuSeparator = function(config){
3914     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3915 };
3916
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3918     
3919     getAutoCreate : function(){
3920         var cfg = {
3921             cls: 'divider',
3922             tag : 'li'
3923         };
3924         
3925         return cfg;
3926     }
3927    
3928 });
3929
3930  
3931
3932  
3933 /*
3934 * Licence: LGPL
3935 */
3936
3937 /**
3938  * @class Roo.bootstrap.Modal
3939  * @extends Roo.bootstrap.Component
3940  * Bootstrap Modal class
3941  * @cfg {String} title Title of dialog
3942  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3944  * @cfg {Boolean} specificTitle default false
3945  * @cfg {Array} buttons Array of buttons or standard button set..
3946  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947  * @cfg {Boolean} animate default true
3948  * @cfg {Boolean} allow_close default true
3949  * @cfg {Boolean} fitwindow default false
3950  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3951  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3952  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3953  * @cfg {String} size (sm|lg|xl) default empty
3954  * @cfg {Number} max_width set the max width of modal
3955  * @cfg {Boolean} editableTitle can the title be edited
3956
3957  *
3958  *
3959  * @constructor
3960  * Create a new Modal Dialog
3961  * @param {Object} config The config object
3962  */
3963
3964 Roo.bootstrap.Modal = function(config){
3965     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3966     this.addEvents({
3967         // raw events
3968         /**
3969          * @event btnclick
3970          * The raw btnclick event for the button
3971          * @param {Roo.EventObject} e
3972          */
3973         "btnclick" : true,
3974         /**
3975          * @event resize
3976          * Fire when dialog resize
3977          * @param {Roo.bootstrap.Modal} this
3978          * @param {Roo.EventObject} e
3979          */
3980         "resize" : true,
3981         /**
3982          * @event titlechanged
3983          * Fire when the editable title has been changed
3984          * @param {Roo.bootstrap.Modal} this
3985          * @param {Roo.EventObject} value
3986          */
3987         "titlechanged" : true 
3988         
3989     });
3990     this.buttons = this.buttons || [];
3991
3992     if (this.tmpl) {
3993         this.tmpl = Roo.factory(this.tmpl);
3994     }
3995
3996 };
3997
3998 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3999
4000     title : 'test dialog',
4001
4002     buttons : false,
4003
4004     // set on load...
4005
4006     html: false,
4007
4008     tmp: false,
4009
4010     specificTitle: false,
4011
4012     buttonPosition: 'right',
4013
4014     allow_close : true,
4015
4016     animate : true,
4017
4018     fitwindow: false,
4019     
4020      // private
4021     dialogEl: false,
4022     bodyEl:  false,
4023     footerEl:  false,
4024     titleEl:  false,
4025     closeEl:  false,
4026
4027     size: '',
4028     
4029     max_width: 0,
4030     
4031     max_height: 0,
4032     
4033     fit_content: false,
4034     editableTitle  : false,
4035
4036     onRender : function(ct, position)
4037     {
4038         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4039
4040         if(!this.el){
4041             var cfg = Roo.apply({},  this.getAutoCreate());
4042             cfg.id = Roo.id();
4043             //if(!cfg.name){
4044             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4045             //}
4046             //if (!cfg.name.length) {
4047             //    delete cfg.name;
4048            // }
4049             if (this.cls) {
4050                 cfg.cls += ' ' + this.cls;
4051             }
4052             if (this.style) {
4053                 cfg.style = this.style;
4054             }
4055             this.el = Roo.get(document.body).createChild(cfg, position);
4056         }
4057         //var type = this.el.dom.type;
4058
4059
4060         if(this.tabIndex !== undefined){
4061             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4062         }
4063
4064         this.dialogEl = this.el.select('.modal-dialog',true).first();
4065         this.bodyEl = this.el.select('.modal-body',true).first();
4066         this.closeEl = this.el.select('.modal-header .close', true).first();
4067         this.headerEl = this.el.select('.modal-header',true).first();
4068         this.titleEl = this.el.select('.modal-title',true).first();
4069         this.footerEl = this.el.select('.modal-footer',true).first();
4070
4071         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4072         
4073         //this.el.addClass("x-dlg-modal");
4074
4075         if (this.buttons.length) {
4076             Roo.each(this.buttons, function(bb) {
4077                 var b = Roo.apply({}, bb);
4078                 b.xns = b.xns || Roo.bootstrap;
4079                 b.xtype = b.xtype || 'Button';
4080                 if (typeof(b.listeners) == 'undefined') {
4081                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4082                 }
4083
4084                 var btn = Roo.factory(b);
4085
4086                 btn.render(this.getButtonContainer());
4087
4088             },this);
4089         }
4090         // render the children.
4091         var nitems = [];
4092
4093         if(typeof(this.items) != 'undefined'){
4094             var items = this.items;
4095             delete this.items;
4096
4097             for(var i =0;i < items.length;i++) {
4098                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4099             }
4100         }
4101
4102         this.items = nitems;
4103
4104         // where are these used - they used to be body/close/footer
4105
4106
4107         this.initEvents();
4108         //this.el.addClass([this.fieldClass, this.cls]);
4109
4110     },
4111
4112     getAutoCreate : function()
4113     {
4114         // we will default to modal-body-overflow - might need to remove or make optional later.
4115         var bdy = {
4116                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4117                 html : this.html || ''
4118         };
4119
4120         var title = {
4121             tag: 'h5',
4122             cls : 'modal-title',
4123             html : this.title
4124         };
4125
4126         if(this.specificTitle){ // WTF is this?
4127             title = this.title;
4128         }
4129
4130         var header = [];
4131         if (this.allow_close && Roo.bootstrap.version == 3) {
4132             header.push({
4133                 tag: 'button',
4134                 cls : 'close',
4135                 html : '&times'
4136             });
4137         }
4138
4139         header.push(title);
4140
4141         if (this.editableTitle) {
4142             header.push({
4143                 cls: 'form-control roo-editable-title d-none',
4144                 tag: 'input',
4145                 type: 'text'
4146             });
4147         }
4148         
4149         if (this.allow_close && Roo.bootstrap.version == 4) {
4150             header.push({
4151                 tag: 'button',
4152                 cls : 'close',
4153                 html : '&times'
4154             });
4155         }
4156         
4157         var size = '';
4158
4159         if(this.size.length){
4160             size = 'modal-' + this.size;
4161         }
4162         
4163         var footer = Roo.bootstrap.version == 3 ?
4164             {
4165                 cls : 'modal-footer',
4166                 cn : [
4167                     {
4168                         tag: 'div',
4169                         cls: 'btn-' + this.buttonPosition
4170                     }
4171                 ]
4172
4173             } :
4174             {  // BS4 uses mr-auto on left buttons....
4175                 cls : 'modal-footer'
4176             };
4177
4178             
4179
4180         
4181         
4182         var modal = {
4183             cls: "modal",
4184              cn : [
4185                 {
4186                     cls: "modal-dialog " + size,
4187                     cn : [
4188                         {
4189                             cls : "modal-content",
4190                             cn : [
4191                                 {
4192                                     cls : 'modal-header',
4193                                     cn : header
4194                                 },
4195                                 bdy,
4196                                 footer
4197                             ]
4198
4199                         }
4200                     ]
4201
4202                 }
4203             ]
4204         };
4205
4206         if(this.animate){
4207             modal.cls += ' fade';
4208         }
4209
4210         return modal;
4211
4212     },
4213     getChildContainer : function() {
4214
4215          return this.bodyEl;
4216
4217     },
4218     getButtonContainer : function() {
4219         
4220          return Roo.bootstrap.version == 4 ?
4221             this.el.select('.modal-footer',true).first()
4222             : this.el.select('.modal-footer div',true).first();
4223
4224     },
4225     initEvents : function()
4226     {
4227         if (this.allow_close) {
4228             this.closeEl.on('click', this.hide, this);
4229         }
4230         Roo.EventManager.onWindowResize(this.resize, this, true);
4231         if (this.editableTitle) {
4232             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4233             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4234             this.headerEditEl.on('keyup', function(e) {
4235                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4236                         this.toggleHeaderInput(false)
4237                     }
4238                 }, this);
4239             this.headerEditEl.on('blur', function(e) {
4240                 this.toggleHeaderInput(false)
4241             },this);
4242         }
4243
4244     },
4245   
4246
4247     resize : function()
4248     {
4249         this.maskEl.setSize(
4250             Roo.lib.Dom.getViewWidth(true),
4251             Roo.lib.Dom.getViewHeight(true)
4252         );
4253         
4254         if (this.fitwindow) {
4255             
4256            this.dialogEl.setStyle( { 'max-width' : '100%' });
4257             this.setSize(
4258                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4259                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4260             );
4261             return;
4262         }
4263         
4264         if(this.max_width !== 0) {
4265             
4266             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4267             
4268             if(this.height) {
4269                 this.setSize(w, this.height);
4270                 return;
4271             }
4272             
4273             if(this.max_height) {
4274                 this.setSize(w,Math.min(
4275                     this.max_height,
4276                     Roo.lib.Dom.getViewportHeight(true) - 60
4277                 ));
4278                 
4279                 return;
4280             }
4281             
4282             if(!this.fit_content) {
4283                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4284                 return;
4285             }
4286             
4287             this.setSize(w, Math.min(
4288                 60 +
4289                 this.headerEl.getHeight() + 
4290                 this.footerEl.getHeight() + 
4291                 this.getChildHeight(this.bodyEl.dom.childNodes),
4292                 Roo.lib.Dom.getViewportHeight(true) - 60)
4293             );
4294         }
4295         
4296     },
4297
4298     setSize : function(w,h)
4299     {
4300         if (!w && !h) {
4301             return;
4302         }
4303         
4304         this.resizeTo(w,h);
4305     },
4306
4307     show : function() {
4308
4309         if (!this.rendered) {
4310             this.render();
4311         }
4312         this.toggleHeaderInput(false);
4313         //this.el.setStyle('display', 'block');
4314         this.el.removeClass('hideing');
4315         this.el.dom.style.display='block';
4316         
4317         Roo.get(document.body).addClass('modal-open');
4318  
4319         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4320             
4321             (function(){
4322                 this.el.addClass('show');
4323                 this.el.addClass('in');
4324             }).defer(50, this);
4325         }else{
4326             this.el.addClass('show');
4327             this.el.addClass('in');
4328         }
4329
4330         // not sure how we can show data in here..
4331         //if (this.tmpl) {
4332         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4333         //}
4334
4335         Roo.get(document.body).addClass("x-body-masked");
4336         
4337         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4338         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4339         this.maskEl.dom.style.display = 'block';
4340         this.maskEl.addClass('show');
4341         
4342         
4343         this.resize();
4344         
4345         this.fireEvent('show', this);
4346
4347         // set zindex here - otherwise it appears to be ignored...
4348         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4349
4350         (function () {
4351             this.items.forEach( function(e) {
4352                 e.layout ? e.layout() : false;
4353
4354             });
4355         }).defer(100,this);
4356
4357     },
4358     hide : function()
4359     {
4360         if(this.fireEvent("beforehide", this) !== false){
4361             
4362             this.maskEl.removeClass('show');
4363             
4364             this.maskEl.dom.style.display = '';
4365             Roo.get(document.body).removeClass("x-body-masked");
4366             this.el.removeClass('in');
4367             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4368
4369             if(this.animate){ // why
4370                 this.el.addClass('hideing');
4371                 this.el.removeClass('show');
4372                 (function(){
4373                     if (!this.el.hasClass('hideing')) {
4374                         return; // it's been shown again...
4375                     }
4376                     
4377                     this.el.dom.style.display='';
4378
4379                     Roo.get(document.body).removeClass('modal-open');
4380                     this.el.removeClass('hideing');
4381                 }).defer(150,this);
4382                 
4383             }else{
4384                 this.el.removeClass('show');
4385                 this.el.dom.style.display='';
4386                 Roo.get(document.body).removeClass('modal-open');
4387
4388             }
4389             this.fireEvent('hide', this);
4390         }
4391     },
4392     isVisible : function()
4393     {
4394         
4395         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4396         
4397     },
4398
4399     addButton : function(str, cb)
4400     {
4401
4402
4403         var b = Roo.apply({}, { html : str } );
4404         b.xns = b.xns || Roo.bootstrap;
4405         b.xtype = b.xtype || 'Button';
4406         if (typeof(b.listeners) == 'undefined') {
4407             b.listeners = { click : cb.createDelegate(this)  };
4408         }
4409
4410         var btn = Roo.factory(b);
4411
4412         btn.render(this.getButtonContainer());
4413
4414         return btn;
4415
4416     },
4417
4418     setDefaultButton : function(btn)
4419     {
4420         //this.el.select('.modal-footer').()
4421     },
4422
4423     resizeTo: function(w,h)
4424     {
4425         this.dialogEl.setWidth(w);
4426         
4427         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4428
4429         this.bodyEl.setHeight(h - diff);
4430         
4431         this.fireEvent('resize', this);
4432     },
4433     
4434     setContentSize  : function(w, h)
4435     {
4436
4437     },
4438     onButtonClick: function(btn,e)
4439     {
4440         //Roo.log([a,b,c]);
4441         this.fireEvent('btnclick', btn.name, e);
4442     },
4443      /**
4444      * Set the title of the Dialog
4445      * @param {String} str new Title
4446      */
4447     setTitle: function(str) {
4448         this.titleEl.dom.innerHTML = str;
4449         this.title = str;
4450     },
4451     /**
4452      * Set the body of the Dialog
4453      * @param {String} str new Title
4454      */
4455     setBody: function(str) {
4456         this.bodyEl.dom.innerHTML = str;
4457     },
4458     /**
4459      * Set the body of the Dialog using the template
4460      * @param {Obj} data - apply this data to the template and replace the body contents.
4461      */
4462     applyBody: function(obj)
4463     {
4464         if (!this.tmpl) {
4465             Roo.log("Error - using apply Body without a template");
4466             //code
4467         }
4468         this.tmpl.overwrite(this.bodyEl, obj);
4469     },
4470     
4471     getChildHeight : function(child_nodes)
4472     {
4473         if(
4474             !child_nodes ||
4475             child_nodes.length == 0
4476         ) {
4477             return 0;
4478         }
4479         
4480         var child_height = 0;
4481         
4482         for(var i = 0; i < child_nodes.length; i++) {
4483             
4484             /*
4485             * for modal with tabs...
4486             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4487                 
4488                 var layout_childs = child_nodes[i].childNodes;
4489                 
4490                 for(var j = 0; j < layout_childs.length; j++) {
4491                     
4492                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4493                         
4494                         var layout_body_childs = layout_childs[j].childNodes;
4495                         
4496                         for(var k = 0; k < layout_body_childs.length; k++) {
4497                             
4498                             if(layout_body_childs[k].classList.contains('navbar')) {
4499                                 child_height += layout_body_childs[k].offsetHeight;
4500                                 continue;
4501                             }
4502                             
4503                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4504                                 
4505                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4506                                 
4507                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4508                                     
4509                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4510                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4511                                         continue;
4512                                     }
4513                                     
4514                                 }
4515                                 
4516                             }
4517                             
4518                         }
4519                     }
4520                 }
4521                 continue;
4522             }
4523             */
4524             
4525             child_height += child_nodes[i].offsetHeight;
4526             // Roo.log(child_nodes[i].offsetHeight);
4527         }
4528         
4529         return child_height;
4530     },
4531     toggleHeaderInput : function(is_edit)
4532     {
4533         if (!this.editableTitle) {
4534             return; // not editable.
4535         }
4536         if (is_edit && this.is_header_editing) {
4537             return; // already editing..
4538         }
4539         if (is_edit) {
4540     
4541             this.headerEditEl.dom.value = this.title;
4542             this.headerEditEl.removeClass('d-none');
4543             this.headerEditEl.dom.focus();
4544             this.titleEl.addClass('d-none');
4545             
4546             this.is_header_editing = true;
4547             return
4548         }
4549         // flip back to not editing.
4550         this.title = this.headerEditEl.dom.value;
4551         this.headerEditEl.addClass('d-none');
4552         this.titleEl.removeClass('d-none');
4553         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4554         this.is_header_editing = false;
4555         this.fireEvent('titlechanged', this, this.title);
4556     
4557             
4558         
4559     }
4560
4561 });
4562
4563
4564 Roo.apply(Roo.bootstrap.Modal,  {
4565     /**
4566          * Button config that displays a single OK button
4567          * @type Object
4568          */
4569         OK :  [{
4570             name : 'ok',
4571             weight : 'primary',
4572             html : 'OK'
4573         }],
4574         /**
4575          * Button config that displays Yes and No buttons
4576          * @type Object
4577          */
4578         YESNO : [
4579             {
4580                 name  : 'no',
4581                 html : 'No'
4582             },
4583             {
4584                 name  :'yes',
4585                 weight : 'primary',
4586                 html : 'Yes'
4587             }
4588         ],
4589
4590         /**
4591          * Button config that displays OK and Cancel buttons
4592          * @type Object
4593          */
4594         OKCANCEL : [
4595             {
4596                name : 'cancel',
4597                 html : 'Cancel'
4598             },
4599             {
4600                 name : 'ok',
4601                 weight : 'primary',
4602                 html : 'OK'
4603             }
4604         ],
4605         /**
4606          * Button config that displays Yes, No and Cancel buttons
4607          * @type Object
4608          */
4609         YESNOCANCEL : [
4610             {
4611                 name : 'yes',
4612                 weight : 'primary',
4613                 html : 'Yes'
4614             },
4615             {
4616                 name : 'no',
4617                 html : 'No'
4618             },
4619             {
4620                 name : 'cancel',
4621                 html : 'Cancel'
4622             }
4623         ],
4624         
4625         zIndex : 10001
4626 });
4627
4628 /*
4629  * - LGPL
4630  *
4631  * messagebox - can be used as a replace
4632  * 
4633  */
4634 /**
4635  * @class Roo.MessageBox
4636  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4637  * Example usage:
4638  *<pre><code>
4639 // Basic alert:
4640 Roo.Msg.alert('Status', 'Changes saved successfully.');
4641
4642 // Prompt for user data:
4643 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4644     if (btn == 'ok'){
4645         // process text value...
4646     }
4647 });
4648
4649 // Show a dialog using config options:
4650 Roo.Msg.show({
4651    title:'Save Changes?',
4652    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4653    buttons: Roo.Msg.YESNOCANCEL,
4654    fn: processResult,
4655    animEl: 'elId'
4656 });
4657 </code></pre>
4658  * @singleton
4659  */
4660 Roo.bootstrap.MessageBox = function(){
4661     var dlg, opt, mask, waitTimer;
4662     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4663     var buttons, activeTextEl, bwidth;
4664
4665     
4666     // private
4667     var handleButton = function(button){
4668         dlg.hide();
4669         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4670     };
4671
4672     // private
4673     var handleHide = function(){
4674         if(opt && opt.cls){
4675             dlg.el.removeClass(opt.cls);
4676         }
4677         //if(waitTimer){
4678         //    Roo.TaskMgr.stop(waitTimer);
4679         //    waitTimer = null;
4680         //}
4681     };
4682
4683     // private
4684     var updateButtons = function(b){
4685         var width = 0;
4686         if(!b){
4687             buttons["ok"].hide();
4688             buttons["cancel"].hide();
4689             buttons["yes"].hide();
4690             buttons["no"].hide();
4691             dlg.footerEl.hide();
4692             
4693             return width;
4694         }
4695         dlg.footerEl.show();
4696         for(var k in buttons){
4697             if(typeof buttons[k] != "function"){
4698                 if(b[k]){
4699                     buttons[k].show();
4700                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4701                     width += buttons[k].el.getWidth()+15;
4702                 }else{
4703                     buttons[k].hide();
4704                 }
4705             }
4706         }
4707         return width;
4708     };
4709
4710     // private
4711     var handleEsc = function(d, k, e){
4712         if(opt && opt.closable !== false){
4713             dlg.hide();
4714         }
4715         if(e){
4716             e.stopEvent();
4717         }
4718     };
4719
4720     return {
4721         /**
4722          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4723          * @return {Roo.BasicDialog} The BasicDialog element
4724          */
4725         getDialog : function(){
4726            if(!dlg){
4727                 dlg = new Roo.bootstrap.Modal( {
4728                     //draggable: true,
4729                     //resizable:false,
4730                     //constraintoviewport:false,
4731                     //fixedcenter:true,
4732                     //collapsible : false,
4733                     //shim:true,
4734                     //modal: true,
4735                 //    width: 'auto',
4736                   //  height:100,
4737                     //buttonAlign:"center",
4738                     closeClick : function(){
4739                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4740                             handleButton("no");
4741                         }else{
4742                             handleButton("cancel");
4743                         }
4744                     }
4745                 });
4746                 dlg.render();
4747                 dlg.on("hide", handleHide);
4748                 mask = dlg.mask;
4749                 //dlg.addKeyListener(27, handleEsc);
4750                 buttons = {};
4751                 this.buttons = buttons;
4752                 var bt = this.buttonText;
4753                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4754                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4755                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4756                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4757                 //Roo.log(buttons);
4758                 bodyEl = dlg.bodyEl.createChild({
4759
4760                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4761                         '<textarea class="roo-mb-textarea"></textarea>' +
4762                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4763                 });
4764                 msgEl = bodyEl.dom.firstChild;
4765                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4766                 textboxEl.enableDisplayMode();
4767                 textboxEl.addKeyListener([10,13], function(){
4768                     if(dlg.isVisible() && opt && opt.buttons){
4769                         if(opt.buttons.ok){
4770                             handleButton("ok");
4771                         }else if(opt.buttons.yes){
4772                             handleButton("yes");
4773                         }
4774                     }
4775                 });
4776                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4777                 textareaEl.enableDisplayMode();
4778                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4779                 progressEl.enableDisplayMode();
4780                 
4781                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4782                 var pf = progressEl.dom.firstChild;
4783                 if (pf) {
4784                     pp = Roo.get(pf.firstChild);
4785                     pp.setHeight(pf.offsetHeight);
4786                 }
4787                 
4788             }
4789             return dlg;
4790         },
4791
4792         /**
4793          * Updates the message box body text
4794          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4795          * the XHTML-compliant non-breaking space character '&amp;#160;')
4796          * @return {Roo.MessageBox} This message box
4797          */
4798         updateText : function(text)
4799         {
4800             if(!dlg.isVisible() && !opt.width){
4801                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4802                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4803             }
4804             msgEl.innerHTML = text || '&#160;';
4805       
4806             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4807             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4808             var w = Math.max(
4809                     Math.min(opt.width || cw , this.maxWidth), 
4810                     Math.max(opt.minWidth || this.minWidth, bwidth)
4811             );
4812             if(opt.prompt){
4813                 activeTextEl.setWidth(w);
4814             }
4815             if(dlg.isVisible()){
4816                 dlg.fixedcenter = false;
4817             }
4818             // to big, make it scroll. = But as usual stupid IE does not support
4819             // !important..
4820             
4821             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4822                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4823                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4824             } else {
4825                 bodyEl.dom.style.height = '';
4826                 bodyEl.dom.style.overflowY = '';
4827             }
4828             if (cw > w) {
4829                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4830             } else {
4831                 bodyEl.dom.style.overflowX = '';
4832             }
4833             
4834             dlg.setContentSize(w, bodyEl.getHeight());
4835             if(dlg.isVisible()){
4836                 dlg.fixedcenter = true;
4837             }
4838             return this;
4839         },
4840
4841         /**
4842          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4843          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4844          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4845          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4846          * @return {Roo.MessageBox} This message box
4847          */
4848         updateProgress : function(value, text){
4849             if(text){
4850                 this.updateText(text);
4851             }
4852             
4853             if (pp) { // weird bug on my firefox - for some reason this is not defined
4854                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4855                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4856             }
4857             return this;
4858         },        
4859
4860         /**
4861          * Returns true if the message box is currently displayed
4862          * @return {Boolean} True if the message box is visible, else false
4863          */
4864         isVisible : function(){
4865             return dlg && dlg.isVisible();  
4866         },
4867
4868         /**
4869          * Hides the message box if it is displayed
4870          */
4871         hide : function(){
4872             if(this.isVisible()){
4873                 dlg.hide();
4874             }  
4875         },
4876
4877         /**
4878          * Displays a new message box, or reinitializes an existing message box, based on the config options
4879          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4880          * The following config object properties are supported:
4881          * <pre>
4882 Property    Type             Description
4883 ----------  ---------------  ------------------------------------------------------------------------------------
4884 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4885                                    closes (defaults to undefined)
4886 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4887                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4888 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4889                                    progress and wait dialogs will ignore this property and always hide the
4890                                    close button as they can only be closed programmatically.
4891 cls               String           A custom CSS class to apply to the message box element
4892 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4893                                    displayed (defaults to 75)
4894 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4895                                    function will be btn (the name of the button that was clicked, if applicable,
4896                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4897                                    Progress and wait dialogs will ignore this option since they do not respond to
4898                                    user actions and can only be closed programmatically, so any required function
4899                                    should be called by the same code after it closes the dialog.
4900 icon              String           A CSS class that provides a background image to be used as an icon for
4901                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4902 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4903 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4904 modal             Boolean          False to allow user interaction with the page while the message box is
4905                                    displayed (defaults to true)
4906 msg               String           A string that will replace the existing message box body text (defaults
4907                                    to the XHTML-compliant non-breaking space character '&#160;')
4908 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4909 progress          Boolean          True to display a progress bar (defaults to false)
4910 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4911 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4912 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4913 title             String           The title text
4914 value             String           The string value to set into the active textbox element if displayed
4915 wait              Boolean          True to display a progress bar (defaults to false)
4916 width             Number           The width of the dialog in pixels
4917 </pre>
4918          *
4919          * Example usage:
4920          * <pre><code>
4921 Roo.Msg.show({
4922    title: 'Address',
4923    msg: 'Please enter your address:',
4924    width: 300,
4925    buttons: Roo.MessageBox.OKCANCEL,
4926    multiline: true,
4927    fn: saveAddress,
4928    animEl: 'addAddressBtn'
4929 });
4930 </code></pre>
4931          * @param {Object} config Configuration options
4932          * @return {Roo.MessageBox} This message box
4933          */
4934         show : function(options)
4935         {
4936             
4937             // this causes nightmares if you show one dialog after another
4938             // especially on callbacks..
4939              
4940             if(this.isVisible()){
4941                 
4942                 this.hide();
4943                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4944                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4945                 Roo.log("New Dialog Message:" +  options.msg )
4946                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4947                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4948                 
4949             }
4950             var d = this.getDialog();
4951             opt = options;
4952             d.setTitle(opt.title || "&#160;");
4953             d.closeEl.setDisplayed(opt.closable !== false);
4954             activeTextEl = textboxEl;
4955             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4956             if(opt.prompt){
4957                 if(opt.multiline){
4958                     textboxEl.hide();
4959                     textareaEl.show();
4960                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4961                         opt.multiline : this.defaultTextHeight);
4962                     activeTextEl = textareaEl;
4963                 }else{
4964                     textboxEl.show();
4965                     textareaEl.hide();
4966                 }
4967             }else{
4968                 textboxEl.hide();
4969                 textareaEl.hide();
4970             }
4971             progressEl.setDisplayed(opt.progress === true);
4972             if (opt.progress) {
4973                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4974             }
4975             this.updateProgress(0);
4976             activeTextEl.dom.value = opt.value || "";
4977             if(opt.prompt){
4978                 dlg.setDefaultButton(activeTextEl);
4979             }else{
4980                 var bs = opt.buttons;
4981                 var db = null;
4982                 if(bs && bs.ok){
4983                     db = buttons["ok"];
4984                 }else if(bs && bs.yes){
4985                     db = buttons["yes"];
4986                 }
4987                 dlg.setDefaultButton(db);
4988             }
4989             bwidth = updateButtons(opt.buttons);
4990             this.updateText(opt.msg);
4991             if(opt.cls){
4992                 d.el.addClass(opt.cls);
4993             }
4994             d.proxyDrag = opt.proxyDrag === true;
4995             d.modal = opt.modal !== false;
4996             d.mask = opt.modal !== false ? mask : false;
4997             if(!d.isVisible()){
4998                 // force it to the end of the z-index stack so it gets a cursor in FF
4999                 document.body.appendChild(dlg.el.dom);
5000                 d.animateTarget = null;
5001                 d.show(options.animEl);
5002             }
5003             return this;
5004         },
5005
5006         /**
5007          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5008          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5009          * and closing the message box when the process is complete.
5010          * @param {String} title The title bar text
5011          * @param {String} msg The message box body text
5012          * @return {Roo.MessageBox} This message box
5013          */
5014         progress : function(title, msg){
5015             this.show({
5016                 title : title,
5017                 msg : msg,
5018                 buttons: false,
5019                 progress:true,
5020                 closable:false,
5021                 minWidth: this.minProgressWidth,
5022                 modal : true
5023             });
5024             return this;
5025         },
5026
5027         /**
5028          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5029          * If a callback function is passed it will be called after the user clicks the button, and the
5030          * id of the button that was clicked will be passed as the only parameter to the callback
5031          * (could also be the top-right close button).
5032          * @param {String} title The title bar text
5033          * @param {String} msg The message box body text
5034          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5035          * @param {Object} scope (optional) The scope of the callback function
5036          * @return {Roo.MessageBox} This message box
5037          */
5038         alert : function(title, msg, fn, scope)
5039         {
5040             this.show({
5041                 title : title,
5042                 msg : msg,
5043                 buttons: this.OK,
5044                 fn: fn,
5045                 closable : false,
5046                 scope : scope,
5047                 modal : true
5048             });
5049             return this;
5050         },
5051
5052         /**
5053          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5054          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5055          * You are responsible for closing the message box when the process is complete.
5056          * @param {String} msg The message box body text
5057          * @param {String} title (optional) The title bar text
5058          * @return {Roo.MessageBox} This message box
5059          */
5060         wait : function(msg, title){
5061             this.show({
5062                 title : title,
5063                 msg : msg,
5064                 buttons: false,
5065                 closable:false,
5066                 progress:true,
5067                 modal:true,
5068                 width:300,
5069                 wait:true
5070             });
5071             waitTimer = Roo.TaskMgr.start({
5072                 run: function(i){
5073                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5074                 },
5075                 interval: 1000
5076             });
5077             return this;
5078         },
5079
5080         /**
5081          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5082          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5083          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5084          * @param {String} title The title bar text
5085          * @param {String} msg The message box body text
5086          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5087          * @param {Object} scope (optional) The scope of the callback function
5088          * @return {Roo.MessageBox} This message box
5089          */
5090         confirm : function(title, msg, fn, scope){
5091             this.show({
5092                 title : title,
5093                 msg : msg,
5094                 buttons: this.YESNO,
5095                 fn: fn,
5096                 scope : scope,
5097                 modal : true
5098             });
5099             return this;
5100         },
5101
5102         /**
5103          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5104          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5105          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5106          * (could also be the top-right close button) and the text that was entered will be passed as the two
5107          * parameters to the callback.
5108          * @param {String} title The title bar text
5109          * @param {String} msg The message box body text
5110          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5111          * @param {Object} scope (optional) The scope of the callback function
5112          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5113          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5114          * @return {Roo.MessageBox} This message box
5115          */
5116         prompt : function(title, msg, fn, scope, multiline){
5117             this.show({
5118                 title : title,
5119                 msg : msg,
5120                 buttons: this.OKCANCEL,
5121                 fn: fn,
5122                 minWidth:250,
5123                 scope : scope,
5124                 prompt:true,
5125                 multiline: multiline,
5126                 modal : true
5127             });
5128             return this;
5129         },
5130
5131         /**
5132          * Button config that displays a single OK button
5133          * @type Object
5134          */
5135         OK : {ok:true},
5136         /**
5137          * Button config that displays Yes and No buttons
5138          * @type Object
5139          */
5140         YESNO : {yes:true, no:true},
5141         /**
5142          * Button config that displays OK and Cancel buttons
5143          * @type Object
5144          */
5145         OKCANCEL : {ok:true, cancel:true},
5146         /**
5147          * Button config that displays Yes, No and Cancel buttons
5148          * @type Object
5149          */
5150         YESNOCANCEL : {yes:true, no:true, cancel:true},
5151
5152         /**
5153          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5154          * @type Number
5155          */
5156         defaultTextHeight : 75,
5157         /**
5158          * The maximum width in pixels of the message box (defaults to 600)
5159          * @type Number
5160          */
5161         maxWidth : 600,
5162         /**
5163          * The minimum width in pixels of the message box (defaults to 100)
5164          * @type Number
5165          */
5166         minWidth : 100,
5167         /**
5168          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5169          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5170          * @type Number
5171          */
5172         minProgressWidth : 250,
5173         /**
5174          * An object containing the default button text strings that can be overriden for localized language support.
5175          * Supported properties are: ok, cancel, yes and no.
5176          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5177          * @type Object
5178          */
5179         buttonText : {
5180             ok : "OK",
5181             cancel : "Cancel",
5182             yes : "Yes",
5183             no : "No"
5184         }
5185     };
5186 }();
5187
5188 /**
5189  * Shorthand for {@link Roo.MessageBox}
5190  */
5191 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5192 Roo.Msg = Roo.Msg || Roo.MessageBox;
5193 /*
5194  * - LGPL
5195  *
5196  * navbar
5197  * 
5198  */
5199
5200 /**
5201  * @class Roo.bootstrap.Navbar
5202  * @extends Roo.bootstrap.Component
5203  * Bootstrap Navbar class
5204
5205  * @constructor
5206  * Create a new Navbar
5207  * @param {Object} config The config object
5208  */
5209
5210
5211 Roo.bootstrap.Navbar = function(config){
5212     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5213     this.addEvents({
5214         // raw events
5215         /**
5216          * @event beforetoggle
5217          * Fire before toggle the menu
5218          * @param {Roo.EventObject} e
5219          */
5220         "beforetoggle" : true
5221     });
5222 };
5223
5224 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5225     
5226     
5227    
5228     // private
5229     navItems : false,
5230     loadMask : false,
5231     
5232     
5233     getAutoCreate : function(){
5234         
5235         
5236         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5237         
5238     },
5239     
5240     initEvents :function ()
5241     {
5242         //Roo.log(this.el.select('.navbar-toggle',true));
5243         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5244         
5245         var mark = {
5246             tag: "div",
5247             cls:"x-dlg-mask"
5248         };
5249         
5250         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5251         
5252         var size = this.el.getSize();
5253         this.maskEl.setSize(size.width, size.height);
5254         this.maskEl.enableDisplayMode("block");
5255         this.maskEl.hide();
5256         
5257         if(this.loadMask){
5258             this.maskEl.show();
5259         }
5260     },
5261     
5262     
5263     getChildContainer : function()
5264     {
5265         if (this.el && this.el.select('.collapse').getCount()) {
5266             return this.el.select('.collapse',true).first();
5267         }
5268         
5269         return this.el;
5270     },
5271     
5272     mask : function()
5273     {
5274         this.maskEl.show();
5275     },
5276     
5277     unmask : function()
5278     {
5279         this.maskEl.hide();
5280     },
5281     onToggle : function()
5282     {
5283         
5284         if(this.fireEvent('beforetoggle', this) === false){
5285             return;
5286         }
5287         var ce = this.el.select('.navbar-collapse',true).first();
5288       
5289         if (!ce.hasClass('show')) {
5290            this.expand();
5291         } else {
5292             this.collapse();
5293         }
5294         
5295         
5296     
5297     },
5298     /**
5299      * Expand the navbar pulldown 
5300      */
5301     expand : function ()
5302     {
5303        
5304         var ce = this.el.select('.navbar-collapse',true).first();
5305         if (ce.hasClass('collapsing')) {
5306             return;
5307         }
5308         ce.dom.style.height = '';
5309                // show it...
5310         ce.addClass('in'); // old...
5311         ce.removeClass('collapse');
5312         ce.addClass('show');
5313         var h = ce.getHeight();
5314         Roo.log(h);
5315         ce.removeClass('show');
5316         // at this point we should be able to see it..
5317         ce.addClass('collapsing');
5318         
5319         ce.setHeight(0); // resize it ...
5320         ce.on('transitionend', function() {
5321             //Roo.log('done transition');
5322             ce.removeClass('collapsing');
5323             ce.addClass('show');
5324             ce.removeClass('collapse');
5325
5326             ce.dom.style.height = '';
5327         }, this, { single: true} );
5328         ce.setHeight(h);
5329         ce.dom.scrollTop = 0;
5330     },
5331     /**
5332      * Collapse the navbar pulldown 
5333      */
5334     collapse : function()
5335     {
5336          var ce = this.el.select('.navbar-collapse',true).first();
5337        
5338         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5339             // it's collapsed or collapsing..
5340             return;
5341         }
5342         ce.removeClass('in'); // old...
5343         ce.setHeight(ce.getHeight());
5344         ce.removeClass('show');
5345         ce.addClass('collapsing');
5346         
5347         ce.on('transitionend', function() {
5348             ce.dom.style.height = '';
5349             ce.removeClass('collapsing');
5350             ce.addClass('collapse');
5351         }, this, { single: true} );
5352         ce.setHeight(0);
5353     }
5354     
5355     
5356     
5357 });
5358
5359
5360
5361  
5362
5363  /*
5364  * - LGPL
5365  *
5366  * navbar
5367  * 
5368  */
5369
5370 /**
5371  * @class Roo.bootstrap.NavSimplebar
5372  * @extends Roo.bootstrap.Navbar
5373  * Bootstrap Sidebar class
5374  *
5375  * @cfg {Boolean} inverse is inverted color
5376  * 
5377  * @cfg {String} type (nav | pills | tabs)
5378  * @cfg {Boolean} arrangement stacked | justified
5379  * @cfg {String} align (left | right) alignment
5380  * 
5381  * @cfg {Boolean} main (true|false) main nav bar? default false
5382  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5383  * 
5384  * @cfg {String} tag (header|footer|nav|div) default is nav 
5385
5386  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5387  * 
5388  * 
5389  * @constructor
5390  * Create a new Sidebar
5391  * @param {Object} config The config object
5392  */
5393
5394
5395 Roo.bootstrap.NavSimplebar = function(config){
5396     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5397 };
5398
5399 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5400     
5401     inverse: false,
5402     
5403     type: false,
5404     arrangement: '',
5405     align : false,
5406     
5407     weight : 'light',
5408     
5409     main : false,
5410     
5411     
5412     tag : false,
5413     
5414     
5415     getAutoCreate : function(){
5416         
5417         
5418         var cfg = {
5419             tag : this.tag || 'div',
5420             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5421         };
5422         if (['light','white'].indexOf(this.weight) > -1) {
5423             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5424         }
5425         cfg.cls += ' bg-' + this.weight;
5426         
5427         if (this.inverse) {
5428             cfg.cls += ' navbar-inverse';
5429             
5430         }
5431         
5432         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5433         
5434         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5435             return cfg;
5436         }
5437         
5438         
5439     
5440         
5441         cfg.cn = [
5442             {
5443                 cls: 'nav nav-' + this.xtype,
5444                 tag : 'ul'
5445             }
5446         ];
5447         
5448          
5449         this.type = this.type || 'nav';
5450         if (['tabs','pills'].indexOf(this.type) != -1) {
5451             cfg.cn[0].cls += ' nav-' + this.type
5452         
5453         
5454         } else {
5455             if (this.type!=='nav') {
5456                 Roo.log('nav type must be nav/tabs/pills')
5457             }
5458             cfg.cn[0].cls += ' navbar-nav'
5459         }
5460         
5461         
5462         
5463         
5464         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5465             cfg.cn[0].cls += ' nav-' + this.arrangement;
5466         }
5467         
5468         
5469         if (this.align === 'right') {
5470             cfg.cn[0].cls += ' navbar-right';
5471         }
5472         
5473         
5474         
5475         
5476         return cfg;
5477     
5478         
5479     }
5480     
5481     
5482     
5483 });
5484
5485
5486
5487  
5488
5489  
5490        /*
5491  * - LGPL
5492  *
5493  * navbar
5494  * navbar-fixed-top
5495  * navbar-expand-md  fixed-top 
5496  */
5497
5498 /**
5499  * @class Roo.bootstrap.NavHeaderbar
5500  * @extends Roo.bootstrap.NavSimplebar
5501  * Bootstrap Sidebar class
5502  *
5503  * @cfg {String} brand what is brand
5504  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5505  * @cfg {String} brand_href href of the brand
5506  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5507  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5508  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5509  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5510  * 
5511  * @constructor
5512  * Create a new Sidebar
5513  * @param {Object} config The config object
5514  */
5515
5516
5517 Roo.bootstrap.NavHeaderbar = function(config){
5518     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5519       
5520 };
5521
5522 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5523     
5524     position: '',
5525     brand: '',
5526     brand_href: false,
5527     srButton : true,
5528     autohide : false,
5529     desktopCenter : false,
5530    
5531     
5532     getAutoCreate : function(){
5533         
5534         var   cfg = {
5535             tag: this.nav || 'nav',
5536             cls: 'navbar navbar-expand-md',
5537             role: 'navigation',
5538             cn: []
5539         };
5540         
5541         var cn = cfg.cn;
5542         if (this.desktopCenter) {
5543             cn.push({cls : 'container', cn : []});
5544             cn = cn[0].cn;
5545         }
5546         
5547         if(this.srButton){
5548             var btn = {
5549                 tag: 'button',
5550                 type: 'button',
5551                 cls: 'navbar-toggle navbar-toggler',
5552                 'data-toggle': 'collapse',
5553                 cn: [
5554                     {
5555                         tag: 'span',
5556                         cls: 'sr-only',
5557                         html: 'Toggle navigation'
5558                     },
5559                     {
5560                         tag: 'span',
5561                         cls: 'icon-bar navbar-toggler-icon'
5562                     },
5563                     {
5564                         tag: 'span',
5565                         cls: 'icon-bar'
5566                     },
5567                     {
5568                         tag: 'span',
5569                         cls: 'icon-bar'
5570                     }
5571                 ]
5572             };
5573             
5574             cn.push( Roo.bootstrap.version == 4 ? btn : {
5575                 tag: 'div',
5576                 cls: 'navbar-header',
5577                 cn: [
5578                     btn
5579                 ]
5580             });
5581         }
5582         
5583         cn.push({
5584             tag: 'div',
5585             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5586             cn : []
5587         });
5588         
5589         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5590         
5591         if (['light','white'].indexOf(this.weight) > -1) {
5592             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5593         }
5594         cfg.cls += ' bg-' + this.weight;
5595         
5596         
5597         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5598             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5599             
5600             // tag can override this..
5601             
5602             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5603         }
5604         
5605         if (this.brand !== '') {
5606             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5607             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5608                 tag: 'a',
5609                 href: this.brand_href ? this.brand_href : '#',
5610                 cls: 'navbar-brand',
5611                 cn: [
5612                 this.brand
5613                 ]
5614             });
5615         }
5616         
5617         if(this.main){
5618             cfg.cls += ' main-nav';
5619         }
5620         
5621         
5622         return cfg;
5623
5624         
5625     },
5626     getHeaderChildContainer : function()
5627     {
5628         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5629             return this.el.select('.navbar-header',true).first();
5630         }
5631         
5632         return this.getChildContainer();
5633     },
5634     
5635     getChildContainer : function()
5636     {
5637          
5638         return this.el.select('.roo-navbar-collapse',true).first();
5639          
5640         
5641     },
5642     
5643     initEvents : function()
5644     {
5645         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5646         
5647         if (this.autohide) {
5648             
5649             var prevScroll = 0;
5650             var ft = this.el;
5651             
5652             Roo.get(document).on('scroll',function(e) {
5653                 var ns = Roo.get(document).getScroll().top;
5654                 var os = prevScroll;
5655                 prevScroll = ns;
5656                 
5657                 if(ns > os){
5658                     ft.removeClass('slideDown');
5659                     ft.addClass('slideUp');
5660                     return;
5661                 }
5662                 ft.removeClass('slideUp');
5663                 ft.addClass('slideDown');
5664                  
5665               
5666           },this);
5667         }
5668     }    
5669     
5670 });
5671
5672
5673
5674  
5675
5676  /*
5677  * - LGPL
5678  *
5679  * navbar
5680  * 
5681  */
5682
5683 /**
5684  * @class Roo.bootstrap.NavSidebar
5685  * @extends Roo.bootstrap.Navbar
5686  * Bootstrap Sidebar class
5687  * 
5688  * @constructor
5689  * Create a new Sidebar
5690  * @param {Object} config The config object
5691  */
5692
5693
5694 Roo.bootstrap.NavSidebar = function(config){
5695     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5696 };
5697
5698 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5699     
5700     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5701     
5702     getAutoCreate : function(){
5703         
5704         
5705         return  {
5706             tag: 'div',
5707             cls: 'sidebar sidebar-nav'
5708         };
5709     
5710         
5711     }
5712     
5713     
5714     
5715 });
5716
5717
5718
5719  
5720
5721  /*
5722  * - LGPL
5723  *
5724  * nav group
5725  * 
5726  */
5727
5728 /**
5729  * @class Roo.bootstrap.NavGroup
5730  * @extends Roo.bootstrap.Component
5731  * Bootstrap NavGroup class
5732  * @cfg {String} align (left|right)
5733  * @cfg {Boolean} inverse
5734  * @cfg {String} type (nav|pills|tab) default nav
5735  * @cfg {String} navId - reference Id for navbar.
5736  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5737  * 
5738  * @constructor
5739  * Create a new nav group
5740  * @param {Object} config The config object
5741  */
5742
5743 Roo.bootstrap.NavGroup = function(config){
5744     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5745     this.navItems = [];
5746    
5747     Roo.bootstrap.NavGroup.register(this);
5748      this.addEvents({
5749         /**
5750              * @event changed
5751              * Fires when the active item changes
5752              * @param {Roo.bootstrap.NavGroup} this
5753              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5754              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5755          */
5756         'changed': true
5757      });
5758     
5759 };
5760
5761 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5762     
5763     align: '',
5764     inverse: false,
5765     form: false,
5766     type: 'nav',
5767     navId : '',
5768     // private
5769     pilltype : true,
5770     
5771     navItems : false, 
5772     
5773     getAutoCreate : function()
5774     {
5775         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5776         
5777         cfg = {
5778             tag : 'ul',
5779             cls: 'nav' 
5780         };
5781         if (Roo.bootstrap.version == 4) {
5782             if (['tabs','pills'].indexOf(this.type) != -1) {
5783                 cfg.cls += ' nav-' + this.type; 
5784             } else {
5785                 // trying to remove so header bar can right align top?
5786                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5787                     // do not use on header bar... 
5788                     cfg.cls += ' navbar-nav';
5789                 }
5790             }
5791             
5792         } else {
5793             if (['tabs','pills'].indexOf(this.type) != -1) {
5794                 cfg.cls += ' nav-' + this.type
5795             } else {
5796                 if (this.type !== 'nav') {
5797                     Roo.log('nav type must be nav/tabs/pills')
5798                 }
5799                 cfg.cls += ' navbar-nav'
5800             }
5801         }
5802         
5803         if (this.parent() && this.parent().sidebar) {
5804             cfg = {
5805                 tag: 'ul',
5806                 cls: 'dashboard-menu sidebar-menu'
5807             };
5808             
5809             return cfg;
5810         }
5811         
5812         if (this.form === true) {
5813             cfg = {
5814                 tag: 'form',
5815                 cls: 'navbar-form form-inline'
5816             };
5817             //nav navbar-right ml-md-auto
5818             if (this.align === 'right') {
5819                 cfg.cls += ' navbar-right ml-md-auto';
5820             } else {
5821                 cfg.cls += ' navbar-left';
5822             }
5823         }
5824         
5825         if (this.align === 'right') {
5826             cfg.cls += ' navbar-right ml-md-auto';
5827         } else {
5828             cfg.cls += ' mr-auto';
5829         }
5830         
5831         if (this.inverse) {
5832             cfg.cls += ' navbar-inverse';
5833             
5834         }
5835         
5836         
5837         return cfg;
5838     },
5839     /**
5840     * sets the active Navigation item
5841     * @param {Roo.bootstrap.NavItem} the new current navitem
5842     */
5843     setActiveItem : function(item)
5844     {
5845         var prev = false;
5846         Roo.each(this.navItems, function(v){
5847             if (v == item) {
5848                 return ;
5849             }
5850             if (v.isActive()) {
5851                 v.setActive(false, true);
5852                 prev = v;
5853                 
5854             }
5855             
5856         });
5857
5858         item.setActive(true, true);
5859         this.fireEvent('changed', this, item, prev);
5860         
5861         
5862     },
5863     /**
5864     * gets the active Navigation item
5865     * @return {Roo.bootstrap.NavItem} the current navitem
5866     */
5867     getActive : function()
5868     {
5869         
5870         var prev = false;
5871         Roo.each(this.navItems, function(v){
5872             
5873             if (v.isActive()) {
5874                 prev = v;
5875                 
5876             }
5877             
5878         });
5879         return prev;
5880     },
5881     
5882     indexOfNav : function()
5883     {
5884         
5885         var prev = false;
5886         Roo.each(this.navItems, function(v,i){
5887             
5888             if (v.isActive()) {
5889                 prev = i;
5890                 
5891             }
5892             
5893         });
5894         return prev;
5895     },
5896     /**
5897     * adds a Navigation item
5898     * @param {Roo.bootstrap.NavItem} the navitem to add
5899     */
5900     addItem : function(cfg)
5901     {
5902         if (this.form && Roo.bootstrap.version == 4) {
5903             cfg.tag = 'div';
5904         }
5905         var cn = new Roo.bootstrap.NavItem(cfg);
5906         this.register(cn);
5907         cn.parentId = this.id;
5908         cn.onRender(this.el, null);
5909         return cn;
5910     },
5911     /**
5912     * register a Navigation item
5913     * @param {Roo.bootstrap.NavItem} the navitem to add
5914     */
5915     register : function(item)
5916     {
5917         this.navItems.push( item);
5918         item.navId = this.navId;
5919     
5920     },
5921     
5922     /**
5923     * clear all the Navigation item
5924     */
5925    
5926     clearAll : function()
5927     {
5928         this.navItems = [];
5929         this.el.dom.innerHTML = '';
5930     },
5931     
5932     getNavItem: function(tabId)
5933     {
5934         var ret = false;
5935         Roo.each(this.navItems, function(e) {
5936             if (e.tabId == tabId) {
5937                ret =  e;
5938                return false;
5939             }
5940             return true;
5941             
5942         });
5943         return ret;
5944     },
5945     
5946     setActiveNext : function()
5947     {
5948         var i = this.indexOfNav(this.getActive());
5949         if (i > this.navItems.length) {
5950             return;
5951         }
5952         this.setActiveItem(this.navItems[i+1]);
5953     },
5954     setActivePrev : function()
5955     {
5956         var i = this.indexOfNav(this.getActive());
5957         if (i  < 1) {
5958             return;
5959         }
5960         this.setActiveItem(this.navItems[i-1]);
5961     },
5962     clearWasActive : function(except) {
5963         Roo.each(this.navItems, function(e) {
5964             if (e.tabId != except.tabId && e.was_active) {
5965                e.was_active = false;
5966                return false;
5967             }
5968             return true;
5969             
5970         });
5971     },
5972     getWasActive : function ()
5973     {
5974         var r = false;
5975         Roo.each(this.navItems, function(e) {
5976             if (e.was_active) {
5977                r = e;
5978                return false;
5979             }
5980             return true;
5981             
5982         });
5983         return r;
5984     }
5985     
5986     
5987 });
5988
5989  
5990 Roo.apply(Roo.bootstrap.NavGroup, {
5991     
5992     groups: {},
5993      /**
5994     * register a Navigation Group
5995     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5996     */
5997     register : function(navgrp)
5998     {
5999         this.groups[navgrp.navId] = navgrp;
6000         
6001     },
6002     /**
6003     * fetch a Navigation Group based on the navigation ID
6004     * @param {string} the navgroup to add
6005     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6006     */
6007     get: function(navId) {
6008         if (typeof(this.groups[navId]) == 'undefined') {
6009             return false;
6010             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6011         }
6012         return this.groups[navId] ;
6013     }
6014     
6015     
6016     
6017 });
6018
6019  /*
6020  * - LGPL
6021  *
6022  * row
6023  * 
6024  */
6025
6026 /**
6027  * @class Roo.bootstrap.NavItem
6028  * @extends Roo.bootstrap.Component
6029  * Bootstrap Navbar.NavItem class
6030  * @cfg {String} href  link to
6031  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6032  * @cfg {Boolean} button_outline show and outlined button
6033  * @cfg {String} html content of button
6034  * @cfg {String} badge text inside badge
6035  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6036  * @cfg {String} glyphicon DEPRICATED - use fa
6037  * @cfg {String} icon DEPRICATED - use fa
6038  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6039  * @cfg {Boolean} active Is item active
6040  * @cfg {Boolean} disabled Is item disabled
6041  * @cfg {String} linkcls  Link Class
6042  * @cfg {Boolean} preventDefault (true | false) default false
6043  * @cfg {String} tabId the tab that this item activates.
6044  * @cfg {String} tagtype (a|span) render as a href or span?
6045  * @cfg {Boolean} animateRef (true|false) link to element default false  
6046   
6047  * @constructor
6048  * Create a new Navbar Item
6049  * @param {Object} config The config object
6050  */
6051 Roo.bootstrap.NavItem = function(config){
6052     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6053     this.addEvents({
6054         // raw events
6055         /**
6056          * @event click
6057          * The raw click event for the entire grid.
6058          * @param {Roo.EventObject} e
6059          */
6060         "click" : true,
6061          /**
6062             * @event changed
6063             * Fires when the active item active state changes
6064             * @param {Roo.bootstrap.NavItem} this
6065             * @param {boolean} state the new state
6066              
6067          */
6068         'changed': true,
6069         /**
6070             * @event scrollto
6071             * Fires when scroll to element
6072             * @param {Roo.bootstrap.NavItem} this
6073             * @param {Object} options
6074             * @param {Roo.EventObject} e
6075              
6076          */
6077         'scrollto': true
6078     });
6079    
6080 };
6081
6082 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6083     
6084     href: false,
6085     html: '',
6086     badge: '',
6087     icon: false,
6088     fa : false,
6089     glyphicon: false,
6090     active: false,
6091     preventDefault : false,
6092     tabId : false,
6093     tagtype : 'a',
6094     tag: 'li',
6095     disabled : false,
6096     animateRef : false,
6097     was_active : false,
6098     button_weight : '',
6099     button_outline : false,
6100     linkcls : '',
6101     navLink: false,
6102     
6103     getAutoCreate : function(){
6104          
6105         var cfg = {
6106             tag: this.tag,
6107             cls: 'nav-item'
6108         };
6109         
6110         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6111         
6112         if (this.active) {
6113             cfg.cls +=  ' active' ;
6114         }
6115         if (this.disabled) {
6116             cfg.cls += ' disabled';
6117         }
6118         
6119         // BS4 only?
6120         if (this.button_weight.length) {
6121             cfg.tag = this.href ? 'a' : 'button';
6122             cfg.html = this.html || '';
6123             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6124             if (this.href) {
6125                 cfg.href = this.href;
6126             }
6127             if (this.fa) {
6128                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6129             }
6130             
6131             // menu .. should add dropdown-menu class - so no need for carat..
6132             
6133             if (this.badge !== '') {
6134                  
6135                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6136             }
6137             return cfg;
6138         }
6139         
6140         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6141             cfg.cn = [
6142                 {
6143                     tag: this.tagtype,
6144                     href : this.href || "#",
6145                     html: this.html || ''
6146                 }
6147             ];
6148             if (this.tagtype == 'a') {
6149                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6150         
6151             }
6152             if (this.icon) {
6153                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6154             }
6155             if (this.fa) {
6156                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6157             }
6158             if(this.glyphicon) {
6159                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6160             }
6161             
6162             if (this.menu) {
6163                 
6164                 cfg.cn[0].html += " <span class='caret'></span>";
6165              
6166             }
6167             
6168             if (this.badge !== '') {
6169                  
6170                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6171             }
6172         }
6173         
6174         
6175         
6176         return cfg;
6177     },
6178     onRender : function(ct, position)
6179     {
6180        // Roo.log("Call onRender: " + this.xtype);
6181         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6182             this.tag = 'div';
6183         }
6184         
6185         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6186         this.navLink = this.el.select('.nav-link',true).first();
6187         return ret;
6188     },
6189       
6190     
6191     initEvents: function() 
6192     {
6193         if (typeof (this.menu) != 'undefined') {
6194             this.menu.parentType = this.xtype;
6195             this.menu.triggerEl = this.el;
6196             this.menu = this.addxtype(Roo.apply({}, this.menu));
6197         }
6198         
6199         this.el.on('click', this.onClick, this);
6200         
6201         //if(this.tagtype == 'span'){
6202         //    this.el.select('span',true).on('click', this.onClick, this);
6203         //}
6204        
6205         // at this point parent should be available..
6206         this.parent().register(this);
6207     },
6208     
6209     onClick : function(e)
6210     {
6211         if (e.getTarget('.dropdown-menu-item')) {
6212             // did you click on a menu itemm.... - then don't trigger onclick..
6213             return;
6214         }
6215         
6216         if(
6217                 this.preventDefault || 
6218                 this.href == '#' 
6219         ){
6220             Roo.log("NavItem - prevent Default?");
6221             e.preventDefault();
6222         }
6223         
6224         if (this.disabled) {
6225             return;
6226         }
6227         
6228         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6229         if (tg && tg.transition) {
6230             Roo.log("waiting for the transitionend");
6231             return;
6232         }
6233         
6234         
6235         
6236         //Roo.log("fire event clicked");
6237         if(this.fireEvent('click', this, e) === false){
6238             return;
6239         };
6240         
6241         if(this.tagtype == 'span'){
6242             return;
6243         }
6244         
6245         //Roo.log(this.href);
6246         var ael = this.el.select('a',true).first();
6247         //Roo.log(ael);
6248         
6249         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6250             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6251             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6252                 return; // ignore... - it's a 'hash' to another page.
6253             }
6254             Roo.log("NavItem - prevent Default?");
6255             e.preventDefault();
6256             this.scrollToElement(e);
6257         }
6258         
6259         
6260         var p =  this.parent();
6261    
6262         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6263             if (typeof(p.setActiveItem) !== 'undefined') {
6264                 p.setActiveItem(this);
6265             }
6266         }
6267         
6268         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6269         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6270             // remove the collapsed menu expand...
6271             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6272         }
6273     },
6274     
6275     isActive: function () {
6276         return this.active
6277     },
6278     setActive : function(state, fire, is_was_active)
6279     {
6280         if (this.active && !state && this.navId) {
6281             this.was_active = true;
6282             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6283             if (nv) {
6284                 nv.clearWasActive(this);
6285             }
6286             
6287         }
6288         this.active = state;
6289         
6290         if (!state ) {
6291             this.el.removeClass('active');
6292             this.navLink ? this.navLink.removeClass('active') : false;
6293         } else if (!this.el.hasClass('active')) {
6294             
6295             this.el.addClass('active');
6296             if (Roo.bootstrap.version == 4 && this.navLink ) {
6297                 this.navLink.addClass('active');
6298             }
6299             
6300         }
6301         if (fire) {
6302             this.fireEvent('changed', this, state);
6303         }
6304         
6305         // show a panel if it's registered and related..
6306         
6307         if (!this.navId || !this.tabId || !state || is_was_active) {
6308             return;
6309         }
6310         
6311         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6312         if (!tg) {
6313             return;
6314         }
6315         var pan = tg.getPanelByName(this.tabId);
6316         if (!pan) {
6317             return;
6318         }
6319         // if we can not flip to new panel - go back to old nav highlight..
6320         if (false == tg.showPanel(pan)) {
6321             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6322             if (nv) {
6323                 var onav = nv.getWasActive();
6324                 if (onav) {
6325                     onav.setActive(true, false, true);
6326                 }
6327             }
6328             
6329         }
6330         
6331         
6332         
6333     },
6334      // this should not be here...
6335     setDisabled : function(state)
6336     {
6337         this.disabled = state;
6338         if (!state ) {
6339             this.el.removeClass('disabled');
6340         } else if (!this.el.hasClass('disabled')) {
6341             this.el.addClass('disabled');
6342         }
6343         
6344     },
6345     
6346     /**
6347      * Fetch the element to display the tooltip on.
6348      * @return {Roo.Element} defaults to this.el
6349      */
6350     tooltipEl : function()
6351     {
6352         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6353     },
6354     
6355     scrollToElement : function(e)
6356     {
6357         var c = document.body;
6358         
6359         /*
6360          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6361          */
6362         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6363             c = document.documentElement;
6364         }
6365         
6366         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6367         
6368         if(!target){
6369             return;
6370         }
6371
6372         var o = target.calcOffsetsTo(c);
6373         
6374         var options = {
6375             target : target,
6376             value : o[1]
6377         };
6378         
6379         this.fireEvent('scrollto', this, options, e);
6380         
6381         Roo.get(c).scrollTo('top', options.value, true);
6382         
6383         return;
6384     }
6385 });
6386  
6387
6388  /*
6389  * - LGPL
6390  *
6391  * sidebar item
6392  *
6393  *  li
6394  *    <span> icon </span>
6395  *    <span> text </span>
6396  *    <span>badge </span>
6397  */
6398
6399 /**
6400  * @class Roo.bootstrap.NavSidebarItem
6401  * @extends Roo.bootstrap.NavItem
6402  * Bootstrap Navbar.NavSidebarItem class
6403  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6404  * {Boolean} open is the menu open
6405  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6406  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6407  * {String} buttonSize (sm|md|lg)the extra classes for the button
6408  * {Boolean} showArrow show arrow next to the text (default true)
6409  * @constructor
6410  * Create a new Navbar Button
6411  * @param {Object} config The config object
6412  */
6413 Roo.bootstrap.NavSidebarItem = function(config){
6414     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6415     this.addEvents({
6416         // raw events
6417         /**
6418          * @event click
6419          * The raw click event for the entire grid.
6420          * @param {Roo.EventObject} e
6421          */
6422         "click" : true,
6423          /**
6424             * @event changed
6425             * Fires when the active item active state changes
6426             * @param {Roo.bootstrap.NavSidebarItem} this
6427             * @param {boolean} state the new state
6428              
6429          */
6430         'changed': true
6431     });
6432    
6433 };
6434
6435 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6436     
6437     badgeWeight : 'default',
6438     
6439     open: false,
6440     
6441     buttonView : false,
6442     
6443     buttonWeight : 'default',
6444     
6445     buttonSize : 'md',
6446     
6447     showArrow : true,
6448     
6449     getAutoCreate : function(){
6450         
6451         
6452         var a = {
6453                 tag: 'a',
6454                 href : this.href || '#',
6455                 cls: '',
6456                 html : '',
6457                 cn : []
6458         };
6459         
6460         if(this.buttonView){
6461             a = {
6462                 tag: 'button',
6463                 href : this.href || '#',
6464                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6465                 html : this.html,
6466                 cn : []
6467             };
6468         }
6469         
6470         var cfg = {
6471             tag: 'li',
6472             cls: '',
6473             cn: [ a ]
6474         };
6475         
6476         if (this.active) {
6477             cfg.cls += ' active';
6478         }
6479         
6480         if (this.disabled) {
6481             cfg.cls += ' disabled';
6482         }
6483         if (this.open) {
6484             cfg.cls += ' open x-open';
6485         }
6486         // left icon..
6487         if (this.glyphicon || this.icon) {
6488             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6489             a.cn.push({ tag : 'i', cls : c }) ;
6490         }
6491         
6492         if(!this.buttonView){
6493             var span = {
6494                 tag: 'span',
6495                 html : this.html || ''
6496             };
6497
6498             a.cn.push(span);
6499             
6500         }
6501         
6502         if (this.badge !== '') {
6503             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6504         }
6505         
6506         if (this.menu) {
6507             
6508             if(this.showArrow){
6509                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6510             }
6511             
6512             a.cls += ' dropdown-toggle treeview' ;
6513         }
6514         
6515         return cfg;
6516     },
6517     
6518     initEvents : function()
6519     { 
6520         if (typeof (this.menu) != 'undefined') {
6521             this.menu.parentType = this.xtype;
6522             this.menu.triggerEl = this.el;
6523             this.menu = this.addxtype(Roo.apply({}, this.menu));
6524         }
6525         
6526         this.el.on('click', this.onClick, this);
6527         
6528         if(this.badge !== ''){
6529             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6530         }
6531         
6532     },
6533     
6534     onClick : function(e)
6535     {
6536         if(this.disabled){
6537             e.preventDefault();
6538             return;
6539         }
6540         
6541         if(this.preventDefault){
6542             e.preventDefault();
6543         }
6544         
6545         this.fireEvent('click', this, e);
6546     },
6547     
6548     disable : function()
6549     {
6550         this.setDisabled(true);
6551     },
6552     
6553     enable : function()
6554     {
6555         this.setDisabled(false);
6556     },
6557     
6558     setDisabled : function(state)
6559     {
6560         if(this.disabled == state){
6561             return;
6562         }
6563         
6564         this.disabled = state;
6565         
6566         if (state) {
6567             this.el.addClass('disabled');
6568             return;
6569         }
6570         
6571         this.el.removeClass('disabled');
6572         
6573         return;
6574     },
6575     
6576     setActive : function(state)
6577     {
6578         if(this.active == state){
6579             return;
6580         }
6581         
6582         this.active = state;
6583         
6584         if (state) {
6585             this.el.addClass('active');
6586             return;
6587         }
6588         
6589         this.el.removeClass('active');
6590         
6591         return;
6592     },
6593     
6594     isActive: function () 
6595     {
6596         return this.active;
6597     },
6598     
6599     setBadge : function(str)
6600     {
6601         if(!this.badgeEl){
6602             return;
6603         }
6604         
6605         this.badgeEl.dom.innerHTML = str;
6606     }
6607     
6608    
6609      
6610  
6611 });
6612  
6613
6614  /*
6615  * - LGPL
6616  *
6617  *  Breadcrumb Nav
6618  * 
6619  */
6620 Roo.namespace('Roo.bootstrap.breadcrumb');
6621
6622
6623 /**
6624  * @class Roo.bootstrap.breadcrumb.Nav
6625  * @extends Roo.bootstrap.Component
6626  * Bootstrap Breadcrumb Nav Class
6627  *  
6628  * @children Roo.bootstrap.breadcrumb.Item
6629  * 
6630  * @constructor
6631  * Create a new breadcrumb.Nav
6632  * @param {Object} config The config object
6633  */
6634
6635
6636 Roo.bootstrap.breadcrumb.Nav = function(config){
6637     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6638     
6639     
6640 };
6641
6642 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6643     
6644     getAutoCreate : function()
6645     {
6646
6647         var cfg = {
6648             tag: 'nav',
6649             cn : [
6650                 {
6651                     tag : 'ol',
6652                     cls : 'breadcrumb'
6653                 }
6654             ]
6655             
6656         };
6657           
6658         return cfg;
6659     },
6660     
6661     initEvents: function()
6662     {
6663         this.olEl = this.el.select('ol',true).first();    
6664     },
6665     getChildContainer : function()
6666     {
6667         return this.olEl;  
6668     }
6669     
6670 });
6671
6672  /*
6673  * - LGPL
6674  *
6675  *  Breadcrumb Item
6676  * 
6677  */
6678
6679
6680 /**
6681  * @class Roo.bootstrap.breadcrumb.Nav
6682  * @extends Roo.bootstrap.Component
6683  * Bootstrap Breadcrumb Nav Class
6684  *  
6685  * @children Roo.bootstrap.breadcrumb.Component
6686  * @cfg {String} html the content of the link.
6687  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6688  * @cfg {Boolean} active is it active
6689
6690  * 
6691  * @constructor
6692  * Create a new breadcrumb.Nav
6693  * @param {Object} config The config object
6694  */
6695
6696 Roo.bootstrap.breadcrumb.Item = function(config){
6697     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6698     this.addEvents({
6699         // img events
6700         /**
6701          * @event click
6702          * The img click event for the img.
6703          * @param {Roo.EventObject} e
6704          */
6705         "click" : true
6706     });
6707     
6708 };
6709
6710 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6711     
6712     href: false,
6713     html : '',
6714     
6715     getAutoCreate : function()
6716     {
6717
6718         var cfg = {
6719             tag: 'li',
6720             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6721         };
6722         if (this.href !== false) {
6723             cfg.cn = [{
6724                 tag : 'a',
6725                 href : this.href,
6726                 html : this.html
6727             }];
6728         } else {
6729             cfg.html = this.html;
6730         }
6731         
6732         return cfg;
6733     },
6734     
6735     initEvents: function()
6736     {
6737         if (this.href) {
6738             this.el.select('a', true).first().on('click',this.onClick, this)
6739         }
6740         
6741     },
6742     onClick : function(e)
6743     {
6744         e.preventDefault();
6745         this.fireEvent('click',this,  e);
6746     }
6747     
6748 });
6749
6750  /*
6751  * - LGPL
6752  *
6753  * row
6754  * 
6755  */
6756
6757 /**
6758  * @class Roo.bootstrap.Row
6759  * @extends Roo.bootstrap.Component
6760  * Bootstrap Row class (contains columns...)
6761  * 
6762  * @constructor
6763  * Create a new Row
6764  * @param {Object} config The config object
6765  */
6766
6767 Roo.bootstrap.Row = function(config){
6768     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6769 };
6770
6771 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6772     
6773     getAutoCreate : function(){
6774        return {
6775             cls: 'row clearfix'
6776        };
6777     }
6778     
6779     
6780 });
6781
6782  
6783
6784  /*
6785  * - LGPL
6786  *
6787  * pagination
6788  * 
6789  */
6790
6791 /**
6792  * @class Roo.bootstrap.Pagination
6793  * @extends Roo.bootstrap.Component
6794  * Bootstrap Pagination class
6795  * @cfg {String} size xs | sm | md | lg
6796  * @cfg {Boolean} inverse false | true
6797  * 
6798  * @constructor
6799  * Create a new Pagination
6800  * @param {Object} config The config object
6801  */
6802
6803 Roo.bootstrap.Pagination = function(config){
6804     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6805 };
6806
6807 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6808     
6809     cls: false,
6810     size: false,
6811     inverse: false,
6812     
6813     getAutoCreate : function(){
6814         var cfg = {
6815             tag: 'ul',
6816                 cls: 'pagination'
6817         };
6818         if (this.inverse) {
6819             cfg.cls += ' inverse';
6820         }
6821         if (this.html) {
6822             cfg.html=this.html;
6823         }
6824         if (this.cls) {
6825             cfg.cls += " " + this.cls;
6826         }
6827         return cfg;
6828     }
6829    
6830 });
6831
6832  
6833
6834  /*
6835  * - LGPL
6836  *
6837  * Pagination item
6838  * 
6839  */
6840
6841
6842 /**
6843  * @class Roo.bootstrap.PaginationItem
6844  * @extends Roo.bootstrap.Component
6845  * Bootstrap PaginationItem class
6846  * @cfg {String} html text
6847  * @cfg {String} href the link
6848  * @cfg {Boolean} preventDefault (true | false) default true
6849  * @cfg {Boolean} active (true | false) default false
6850  * @cfg {Boolean} disabled default false
6851  * 
6852  * 
6853  * @constructor
6854  * Create a new PaginationItem
6855  * @param {Object} config The config object
6856  */
6857
6858
6859 Roo.bootstrap.PaginationItem = function(config){
6860     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6861     this.addEvents({
6862         // raw events
6863         /**
6864          * @event click
6865          * The raw click event for the entire grid.
6866          * @param {Roo.EventObject} e
6867          */
6868         "click" : true
6869     });
6870 };
6871
6872 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6873     
6874     href : false,
6875     html : false,
6876     preventDefault: true,
6877     active : false,
6878     cls : false,
6879     disabled: false,
6880     
6881     getAutoCreate : function(){
6882         var cfg= {
6883             tag: 'li',
6884             cn: [
6885                 {
6886                     tag : 'a',
6887                     href : this.href ? this.href : '#',
6888                     html : this.html ? this.html : ''
6889                 }
6890             ]
6891         };
6892         
6893         if(this.cls){
6894             cfg.cls = this.cls;
6895         }
6896         
6897         if(this.disabled){
6898             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6899         }
6900         
6901         if(this.active){
6902             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6903         }
6904         
6905         return cfg;
6906     },
6907     
6908     initEvents: function() {
6909         
6910         this.el.on('click', this.onClick, this);
6911         
6912     },
6913     onClick : function(e)
6914     {
6915         Roo.log('PaginationItem on click ');
6916         if(this.preventDefault){
6917             e.preventDefault();
6918         }
6919         
6920         if(this.disabled){
6921             return;
6922         }
6923         
6924         this.fireEvent('click', this, e);
6925     }
6926    
6927 });
6928
6929  
6930
6931  /*
6932  * - LGPL
6933  *
6934  * slider
6935  * 
6936  */
6937
6938
6939 /**
6940  * @class Roo.bootstrap.Slider
6941  * @extends Roo.bootstrap.Component
6942  * Bootstrap Slider class
6943  *    
6944  * @constructor
6945  * Create a new Slider
6946  * @param {Object} config The config object
6947  */
6948
6949 Roo.bootstrap.Slider = function(config){
6950     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6951 };
6952
6953 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6954     
6955     getAutoCreate : function(){
6956         
6957         var cfg = {
6958             tag: 'div',
6959             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6960             cn: [
6961                 {
6962                     tag: 'a',
6963                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6964                 }
6965             ]
6966         };
6967         
6968         return cfg;
6969     }
6970    
6971 });
6972
6973  /*
6974  * Based on:
6975  * Ext JS Library 1.1.1
6976  * Copyright(c) 2006-2007, Ext JS, LLC.
6977  *
6978  * Originally Released Under LGPL - original licence link has changed is not relivant.
6979  *
6980  * Fork - LGPL
6981  * <script type="text/javascript">
6982  */
6983  
6984
6985 /**
6986  * @class Roo.grid.ColumnModel
6987  * @extends Roo.util.Observable
6988  * This is the default implementation of a ColumnModel used by the Grid. It defines
6989  * the columns in the grid.
6990  * <br>Usage:<br>
6991  <pre><code>
6992  var colModel = new Roo.grid.ColumnModel([
6993         {header: "Ticker", width: 60, sortable: true, locked: true},
6994         {header: "Company Name", width: 150, sortable: true},
6995         {header: "Market Cap.", width: 100, sortable: true},
6996         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6997         {header: "Employees", width: 100, sortable: true, resizable: false}
6998  ]);
6999  </code></pre>
7000  * <p>
7001  
7002  * The config options listed for this class are options which may appear in each
7003  * individual column definition.
7004  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7005  * @constructor
7006  * @param {Object} config An Array of column config objects. See this class's
7007  * config objects for details.
7008 */
7009 Roo.grid.ColumnModel = function(config){
7010         /**
7011      * The config passed into the constructor
7012      */
7013     this.config = config;
7014     this.lookup = {};
7015
7016     // if no id, create one
7017     // if the column does not have a dataIndex mapping,
7018     // map it to the order it is in the config
7019     for(var i = 0, len = config.length; i < len; i++){
7020         var c = config[i];
7021         if(typeof c.dataIndex == "undefined"){
7022             c.dataIndex = i;
7023         }
7024         if(typeof c.renderer == "string"){
7025             c.renderer = Roo.util.Format[c.renderer];
7026         }
7027         if(typeof c.id == "undefined"){
7028             c.id = Roo.id();
7029         }
7030         if(c.editor && c.editor.xtype){
7031             c.editor  = Roo.factory(c.editor, Roo.grid);
7032         }
7033         if(c.editor && c.editor.isFormField){
7034             c.editor = new Roo.grid.GridEditor(c.editor);
7035         }
7036         this.lookup[c.id] = c;
7037     }
7038
7039     /**
7040      * The width of columns which have no width specified (defaults to 100)
7041      * @type Number
7042      */
7043     this.defaultWidth = 100;
7044
7045     /**
7046      * Default sortable of columns which have no sortable specified (defaults to false)
7047      * @type Boolean
7048      */
7049     this.defaultSortable = false;
7050
7051     this.addEvents({
7052         /**
7053              * @event widthchange
7054              * Fires when the width of a column changes.
7055              * @param {ColumnModel} this
7056              * @param {Number} columnIndex The column index
7057              * @param {Number} newWidth The new width
7058              */
7059             "widthchange": true,
7060         /**
7061              * @event headerchange
7062              * Fires when the text of a header changes.
7063              * @param {ColumnModel} this
7064              * @param {Number} columnIndex The column index
7065              * @param {Number} newText The new header text
7066              */
7067             "headerchange": true,
7068         /**
7069              * @event hiddenchange
7070              * Fires when a column is hidden or "unhidden".
7071              * @param {ColumnModel} this
7072              * @param {Number} columnIndex The column index
7073              * @param {Boolean} hidden true if hidden, false otherwise
7074              */
7075             "hiddenchange": true,
7076             /**
7077          * @event columnmoved
7078          * Fires when a column is moved.
7079          * @param {ColumnModel} this
7080          * @param {Number} oldIndex
7081          * @param {Number} newIndex
7082          */
7083         "columnmoved" : true,
7084         /**
7085          * @event columlockchange
7086          * Fires when a column's locked state is changed
7087          * @param {ColumnModel} this
7088          * @param {Number} colIndex
7089          * @param {Boolean} locked true if locked
7090          */
7091         "columnlockchange" : true
7092     });
7093     Roo.grid.ColumnModel.superclass.constructor.call(this);
7094 };
7095 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7096     /**
7097      * @cfg {String} header The header text to display in the Grid view.
7098      */
7099     /**
7100      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7101      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7102      * specified, the column's index is used as an index into the Record's data Array.
7103      */
7104     /**
7105      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7106      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7107      */
7108     /**
7109      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7110      * Defaults to the value of the {@link #defaultSortable} property.
7111      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7112      */
7113     /**
7114      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7115      */
7116     /**
7117      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7118      */
7119     /**
7120      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7121      */
7122     /**
7123      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7124      */
7125     /**
7126      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7127      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7128      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7129      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7130      */
7131        /**
7132      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7133      */
7134     /**
7135      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7136      */
7137     /**
7138      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7139      */
7140     /**
7141      * @cfg {String} cursor (Optional)
7142      */
7143     /**
7144      * @cfg {String} tooltip (Optional)
7145      */
7146     /**
7147      * @cfg {Number} xs (Optional)
7148      */
7149     /**
7150      * @cfg {Number} sm (Optional)
7151      */
7152     /**
7153      * @cfg {Number} md (Optional)
7154      */
7155     /**
7156      * @cfg {Number} lg (Optional)
7157      */
7158     /**
7159      * Returns the id of the column at the specified index.
7160      * @param {Number} index The column index
7161      * @return {String} the id
7162      */
7163     getColumnId : function(index){
7164         return this.config[index].id;
7165     },
7166
7167     /**
7168      * Returns the column for a specified id.
7169      * @param {String} id The column id
7170      * @return {Object} the column
7171      */
7172     getColumnById : function(id){
7173         return this.lookup[id];
7174     },
7175
7176     
7177     /**
7178      * Returns the column for a specified dataIndex.
7179      * @param {String} dataIndex The column dataIndex
7180      * @return {Object|Boolean} the column or false if not found
7181      */
7182     getColumnByDataIndex: function(dataIndex){
7183         var index = this.findColumnIndex(dataIndex);
7184         return index > -1 ? this.config[index] : false;
7185     },
7186     
7187     /**
7188      * Returns the index for a specified column id.
7189      * @param {String} id The column id
7190      * @return {Number} the index, or -1 if not found
7191      */
7192     getIndexById : function(id){
7193         for(var i = 0, len = this.config.length; i < len; i++){
7194             if(this.config[i].id == id){
7195                 return i;
7196             }
7197         }
7198         return -1;
7199     },
7200     
7201     /**
7202      * Returns the index for a specified column dataIndex.
7203      * @param {String} dataIndex The column dataIndex
7204      * @return {Number} the index, or -1 if not found
7205      */
7206     
7207     findColumnIndex : function(dataIndex){
7208         for(var i = 0, len = this.config.length; i < len; i++){
7209             if(this.config[i].dataIndex == dataIndex){
7210                 return i;
7211             }
7212         }
7213         return -1;
7214     },
7215     
7216     
7217     moveColumn : function(oldIndex, newIndex){
7218         var c = this.config[oldIndex];
7219         this.config.splice(oldIndex, 1);
7220         this.config.splice(newIndex, 0, c);
7221         this.dataMap = null;
7222         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7223     },
7224
7225     isLocked : function(colIndex){
7226         return this.config[colIndex].locked === true;
7227     },
7228
7229     setLocked : function(colIndex, value, suppressEvent){
7230         if(this.isLocked(colIndex) == value){
7231             return;
7232         }
7233         this.config[colIndex].locked = value;
7234         if(!suppressEvent){
7235             this.fireEvent("columnlockchange", this, colIndex, value);
7236         }
7237     },
7238
7239     getTotalLockedWidth : function(){
7240         var totalWidth = 0;
7241         for(var i = 0; i < this.config.length; i++){
7242             if(this.isLocked(i) && !this.isHidden(i)){
7243                 this.totalWidth += this.getColumnWidth(i);
7244             }
7245         }
7246         return totalWidth;
7247     },
7248
7249     getLockedCount : function(){
7250         for(var i = 0, len = this.config.length; i < len; i++){
7251             if(!this.isLocked(i)){
7252                 return i;
7253             }
7254         }
7255         
7256         return this.config.length;
7257     },
7258
7259     /**
7260      * Returns the number of columns.
7261      * @return {Number}
7262      */
7263     getColumnCount : function(visibleOnly){
7264         if(visibleOnly === true){
7265             var c = 0;
7266             for(var i = 0, len = this.config.length; i < len; i++){
7267                 if(!this.isHidden(i)){
7268                     c++;
7269                 }
7270             }
7271             return c;
7272         }
7273         return this.config.length;
7274     },
7275
7276     /**
7277      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7278      * @param {Function} fn
7279      * @param {Object} scope (optional)
7280      * @return {Array} result
7281      */
7282     getColumnsBy : function(fn, scope){
7283         var r = [];
7284         for(var i = 0, len = this.config.length; i < len; i++){
7285             var c = this.config[i];
7286             if(fn.call(scope||this, c, i) === true){
7287                 r[r.length] = c;
7288             }
7289         }
7290         return r;
7291     },
7292
7293     /**
7294      * Returns true if the specified column is sortable.
7295      * @param {Number} col The column index
7296      * @return {Boolean}
7297      */
7298     isSortable : function(col){
7299         if(typeof this.config[col].sortable == "undefined"){
7300             return this.defaultSortable;
7301         }
7302         return this.config[col].sortable;
7303     },
7304
7305     /**
7306      * Returns the rendering (formatting) function defined for the column.
7307      * @param {Number} col The column index.
7308      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7309      */
7310     getRenderer : function(col){
7311         if(!this.config[col].renderer){
7312             return Roo.grid.ColumnModel.defaultRenderer;
7313         }
7314         return this.config[col].renderer;
7315     },
7316
7317     /**
7318      * Sets the rendering (formatting) function for a column.
7319      * @param {Number} col The column index
7320      * @param {Function} fn The function to use to process the cell's raw data
7321      * to return HTML markup for the grid view. The render function is called with
7322      * the following parameters:<ul>
7323      * <li>Data value.</li>
7324      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7325      * <li>css A CSS style string to apply to the table cell.</li>
7326      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7327      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7328      * <li>Row index</li>
7329      * <li>Column index</li>
7330      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7331      */
7332     setRenderer : function(col, fn){
7333         this.config[col].renderer = fn;
7334     },
7335
7336     /**
7337      * Returns the width for the specified column.
7338      * @param {Number} col The column index
7339      * @return {Number}
7340      */
7341     getColumnWidth : function(col){
7342         return this.config[col].width * 1 || this.defaultWidth;
7343     },
7344
7345     /**
7346      * Sets the width for a column.
7347      * @param {Number} col The column index
7348      * @param {Number} width The new width
7349      */
7350     setColumnWidth : function(col, width, suppressEvent){
7351         this.config[col].width = width;
7352         this.totalWidth = null;
7353         if(!suppressEvent){
7354              this.fireEvent("widthchange", this, col, width);
7355         }
7356     },
7357
7358     /**
7359      * Returns the total width of all columns.
7360      * @param {Boolean} includeHidden True to include hidden column widths
7361      * @return {Number}
7362      */
7363     getTotalWidth : function(includeHidden){
7364         if(!this.totalWidth){
7365             this.totalWidth = 0;
7366             for(var i = 0, len = this.config.length; i < len; i++){
7367                 if(includeHidden || !this.isHidden(i)){
7368                     this.totalWidth += this.getColumnWidth(i);
7369                 }
7370             }
7371         }
7372         return this.totalWidth;
7373     },
7374
7375     /**
7376      * Returns the header for the specified column.
7377      * @param {Number} col The column index
7378      * @return {String}
7379      */
7380     getColumnHeader : function(col){
7381         return this.config[col].header;
7382     },
7383
7384     /**
7385      * Sets the header for a column.
7386      * @param {Number} col The column index
7387      * @param {String} header The new header
7388      */
7389     setColumnHeader : function(col, header){
7390         this.config[col].header = header;
7391         this.fireEvent("headerchange", this, col, header);
7392     },
7393
7394     /**
7395      * Returns the tooltip for the specified column.
7396      * @param {Number} col The column index
7397      * @return {String}
7398      */
7399     getColumnTooltip : function(col){
7400             return this.config[col].tooltip;
7401     },
7402     /**
7403      * Sets the tooltip for a column.
7404      * @param {Number} col The column index
7405      * @param {String} tooltip The new tooltip
7406      */
7407     setColumnTooltip : function(col, tooltip){
7408             this.config[col].tooltip = tooltip;
7409     },
7410
7411     /**
7412      * Returns the dataIndex for the specified column.
7413      * @param {Number} col The column index
7414      * @return {Number}
7415      */
7416     getDataIndex : function(col){
7417         return this.config[col].dataIndex;
7418     },
7419
7420     /**
7421      * Sets the dataIndex for a column.
7422      * @param {Number} col The column index
7423      * @param {Number} dataIndex The new dataIndex
7424      */
7425     setDataIndex : function(col, dataIndex){
7426         this.config[col].dataIndex = dataIndex;
7427     },
7428
7429     
7430     
7431     /**
7432      * Returns true if the cell is editable.
7433      * @param {Number} colIndex The column index
7434      * @param {Number} rowIndex The row index - this is nto actually used..?
7435      * @return {Boolean}
7436      */
7437     isCellEditable : function(colIndex, rowIndex){
7438         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7439     },
7440
7441     /**
7442      * Returns the editor defined for the cell/column.
7443      * return false or null to disable editing.
7444      * @param {Number} colIndex The column index
7445      * @param {Number} rowIndex The row index
7446      * @return {Object}
7447      */
7448     getCellEditor : function(colIndex, rowIndex){
7449         return this.config[colIndex].editor;
7450     },
7451
7452     /**
7453      * Sets if a column is editable.
7454      * @param {Number} col The column index
7455      * @param {Boolean} editable True if the column is editable
7456      */
7457     setEditable : function(col, editable){
7458         this.config[col].editable = editable;
7459     },
7460
7461
7462     /**
7463      * Returns true if the column is hidden.
7464      * @param {Number} colIndex The column index
7465      * @return {Boolean}
7466      */
7467     isHidden : function(colIndex){
7468         return this.config[colIndex].hidden;
7469     },
7470
7471
7472     /**
7473      * Returns true if the column width cannot be changed
7474      */
7475     isFixed : function(colIndex){
7476         return this.config[colIndex].fixed;
7477     },
7478
7479     /**
7480      * Returns true if the column can be resized
7481      * @return {Boolean}
7482      */
7483     isResizable : function(colIndex){
7484         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7485     },
7486     /**
7487      * Sets if a column is hidden.
7488      * @param {Number} colIndex The column index
7489      * @param {Boolean} hidden True if the column is hidden
7490      */
7491     setHidden : function(colIndex, hidden){
7492         this.config[colIndex].hidden = hidden;
7493         this.totalWidth = null;
7494         this.fireEvent("hiddenchange", this, colIndex, hidden);
7495     },
7496
7497     /**
7498      * Sets the editor for a column.
7499      * @param {Number} col The column index
7500      * @param {Object} editor The editor object
7501      */
7502     setEditor : function(col, editor){
7503         this.config[col].editor = editor;
7504     }
7505 });
7506
7507 Roo.grid.ColumnModel.defaultRenderer = function(value)
7508 {
7509     if(typeof value == "object") {
7510         return value;
7511     }
7512         if(typeof value == "string" && value.length < 1){
7513             return "&#160;";
7514         }
7515     
7516         return String.format("{0}", value);
7517 };
7518
7519 // Alias for backwards compatibility
7520 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7521 /*
7522  * Based on:
7523  * Ext JS Library 1.1.1
7524  * Copyright(c) 2006-2007, Ext JS, LLC.
7525  *
7526  * Originally Released Under LGPL - original licence link has changed is not relivant.
7527  *
7528  * Fork - LGPL
7529  * <script type="text/javascript">
7530  */
7531  
7532 /**
7533  * @class Roo.LoadMask
7534  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7535  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7536  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7537  * element's UpdateManager load indicator and will be destroyed after the initial load.
7538  * @constructor
7539  * Create a new LoadMask
7540  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7541  * @param {Object} config The config object
7542  */
7543 Roo.LoadMask = function(el, config){
7544     this.el = Roo.get(el);
7545     Roo.apply(this, config);
7546     if(this.store){
7547         this.store.on('beforeload', this.onBeforeLoad, this);
7548         this.store.on('load', this.onLoad, this);
7549         this.store.on('loadexception', this.onLoadException, this);
7550         this.removeMask = false;
7551     }else{
7552         var um = this.el.getUpdateManager();
7553         um.showLoadIndicator = false; // disable the default indicator
7554         um.on('beforeupdate', this.onBeforeLoad, this);
7555         um.on('update', this.onLoad, this);
7556         um.on('failure', this.onLoad, this);
7557         this.removeMask = true;
7558     }
7559 };
7560
7561 Roo.LoadMask.prototype = {
7562     /**
7563      * @cfg {Boolean} removeMask
7564      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7565      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7566      */
7567     /**
7568      * @cfg {String} msg
7569      * The text to display in a centered loading message box (defaults to 'Loading...')
7570      */
7571     msg : 'Loading...',
7572     /**
7573      * @cfg {String} msgCls
7574      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7575      */
7576     msgCls : 'x-mask-loading',
7577
7578     /**
7579      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7580      * @type Boolean
7581      */
7582     disabled: false,
7583
7584     /**
7585      * Disables the mask to prevent it from being displayed
7586      */
7587     disable : function(){
7588        this.disabled = true;
7589     },
7590
7591     /**
7592      * Enables the mask so that it can be displayed
7593      */
7594     enable : function(){
7595         this.disabled = false;
7596     },
7597     
7598     onLoadException : function()
7599     {
7600         Roo.log(arguments);
7601         
7602         if (typeof(arguments[3]) != 'undefined') {
7603             Roo.MessageBox.alert("Error loading",arguments[3]);
7604         } 
7605         /*
7606         try {
7607             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7608                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7609             }   
7610         } catch(e) {
7611             
7612         }
7613         */
7614     
7615         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7616     },
7617     // private
7618     onLoad : function()
7619     {
7620         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7621     },
7622
7623     // private
7624     onBeforeLoad : function(){
7625         if(!this.disabled){
7626             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7627         }
7628     },
7629
7630     // private
7631     destroy : function(){
7632         if(this.store){
7633             this.store.un('beforeload', this.onBeforeLoad, this);
7634             this.store.un('load', this.onLoad, this);
7635             this.store.un('loadexception', this.onLoadException, this);
7636         }else{
7637             var um = this.el.getUpdateManager();
7638             um.un('beforeupdate', this.onBeforeLoad, this);
7639             um.un('update', this.onLoad, this);
7640             um.un('failure', this.onLoad, this);
7641         }
7642     }
7643 };/*
7644  * - LGPL
7645  *
7646  * table
7647  * 
7648  */
7649
7650 /**
7651  * @class Roo.bootstrap.Table
7652  * @extends Roo.bootstrap.Component
7653  * Bootstrap Table class
7654  * @cfg {String} cls table class
7655  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7656  * @cfg {String} bgcolor Specifies the background color for a table
7657  * @cfg {Number} border Specifies whether the table cells should have borders or not
7658  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7659  * @cfg {Number} cellspacing Specifies the space between cells
7660  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7661  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7662  * @cfg {String} sortable Specifies that the table should be sortable
7663  * @cfg {String} summary Specifies a summary of the content of a table
7664  * @cfg {Number} width Specifies the width of a table
7665  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7666  * 
7667  * @cfg {boolean} striped Should the rows be alternative striped
7668  * @cfg {boolean} bordered Add borders to the table
7669  * @cfg {boolean} hover Add hover highlighting
7670  * @cfg {boolean} condensed Format condensed
7671  * @cfg {boolean} responsive Format condensed
7672  * @cfg {Boolean} loadMask (true|false) default false
7673  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7674  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7675  * @cfg {Boolean} rowSelection (true|false) default false
7676  * @cfg {Boolean} cellSelection (true|false) default false
7677  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7678  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7679  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7680  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7681  
7682  * 
7683  * @constructor
7684  * Create a new Table
7685  * @param {Object} config The config object
7686  */
7687
7688 Roo.bootstrap.Table = function(config){
7689     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7690     
7691   
7692     
7693     // BC...
7694     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7695     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7696     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7697     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7698     
7699     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7700     if (this.sm) {
7701         this.sm.grid = this;
7702         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7703         this.sm = this.selModel;
7704         this.sm.xmodule = this.xmodule || false;
7705     }
7706     
7707     if (this.cm && typeof(this.cm.config) == 'undefined') {
7708         this.colModel = new Roo.grid.ColumnModel(this.cm);
7709         this.cm = this.colModel;
7710         this.cm.xmodule = this.xmodule || false;
7711     }
7712     if (this.store) {
7713         this.store= Roo.factory(this.store, Roo.data);
7714         this.ds = this.store;
7715         this.ds.xmodule = this.xmodule || false;
7716          
7717     }
7718     if (this.footer && this.store) {
7719         this.footer.dataSource = this.ds;
7720         this.footer = Roo.factory(this.footer);
7721     }
7722     
7723     /** @private */
7724     this.addEvents({
7725         /**
7726          * @event cellclick
7727          * Fires when a cell is clicked
7728          * @param {Roo.bootstrap.Table} this
7729          * @param {Roo.Element} el
7730          * @param {Number} rowIndex
7731          * @param {Number} columnIndex
7732          * @param {Roo.EventObject} e
7733          */
7734         "cellclick" : true,
7735         /**
7736          * @event celldblclick
7737          * Fires when a cell is double clicked
7738          * @param {Roo.bootstrap.Table} this
7739          * @param {Roo.Element} el
7740          * @param {Number} rowIndex
7741          * @param {Number} columnIndex
7742          * @param {Roo.EventObject} e
7743          */
7744         "celldblclick" : true,
7745         /**
7746          * @event rowclick
7747          * Fires when a row is clicked
7748          * @param {Roo.bootstrap.Table} this
7749          * @param {Roo.Element} el
7750          * @param {Number} rowIndex
7751          * @param {Roo.EventObject} e
7752          */
7753         "rowclick" : true,
7754         /**
7755          * @event rowdblclick
7756          * Fires when a row is double clicked
7757          * @param {Roo.bootstrap.Table} this
7758          * @param {Roo.Element} el
7759          * @param {Number} rowIndex
7760          * @param {Roo.EventObject} e
7761          */
7762         "rowdblclick" : true,
7763         /**
7764          * @event mouseover
7765          * Fires when a mouseover occur
7766          * @param {Roo.bootstrap.Table} this
7767          * @param {Roo.Element} el
7768          * @param {Number} rowIndex
7769          * @param {Number} columnIndex
7770          * @param {Roo.EventObject} e
7771          */
7772         "mouseover" : true,
7773         /**
7774          * @event mouseout
7775          * Fires when a mouseout occur
7776          * @param {Roo.bootstrap.Table} this
7777          * @param {Roo.Element} el
7778          * @param {Number} rowIndex
7779          * @param {Number} columnIndex
7780          * @param {Roo.EventObject} e
7781          */
7782         "mouseout" : true,
7783         /**
7784          * @event rowclass
7785          * Fires when a row is rendered, so you can change add a style to it.
7786          * @param {Roo.bootstrap.Table} this
7787          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7788          */
7789         'rowclass' : true,
7790           /**
7791          * @event rowsrendered
7792          * Fires when all the  rows have been rendered
7793          * @param {Roo.bootstrap.Table} this
7794          */
7795         'rowsrendered' : true,
7796         /**
7797          * @event contextmenu
7798          * The raw contextmenu event for the entire grid.
7799          * @param {Roo.EventObject} e
7800          */
7801         "contextmenu" : true,
7802         /**
7803          * @event rowcontextmenu
7804          * Fires when a row is right clicked
7805          * @param {Roo.bootstrap.Table} this
7806          * @param {Number} rowIndex
7807          * @param {Roo.EventObject} e
7808          */
7809         "rowcontextmenu" : true,
7810         /**
7811          * @event cellcontextmenu
7812          * Fires when a cell is right clicked
7813          * @param {Roo.bootstrap.Table} this
7814          * @param {Number} rowIndex
7815          * @param {Number} cellIndex
7816          * @param {Roo.EventObject} e
7817          */
7818          "cellcontextmenu" : true,
7819          /**
7820          * @event headercontextmenu
7821          * Fires when a header is right clicked
7822          * @param {Roo.bootstrap.Table} this
7823          * @param {Number} columnIndex
7824          * @param {Roo.EventObject} e
7825          */
7826         "headercontextmenu" : true
7827     });
7828 };
7829
7830 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7831     
7832     cls: false,
7833     align: false,
7834     bgcolor: false,
7835     border: false,
7836     cellpadding: false,
7837     cellspacing: false,
7838     frame: false,
7839     rules: false,
7840     sortable: false,
7841     summary: false,
7842     width: false,
7843     striped : false,
7844     scrollBody : false,
7845     bordered: false,
7846     hover:  false,
7847     condensed : false,
7848     responsive : false,
7849     sm : false,
7850     cm : false,
7851     store : false,
7852     loadMask : false,
7853     footerShow : true,
7854     headerShow : true,
7855   
7856     rowSelection : false,
7857     cellSelection : false,
7858     layout : false,
7859     
7860     // Roo.Element - the tbody
7861     mainBody: false,
7862     // Roo.Element - thead element
7863     mainHead: false,
7864     
7865     container: false, // used by gridpanel...
7866     
7867     lazyLoad : false,
7868     
7869     CSS : Roo.util.CSS,
7870     
7871     auto_hide_footer : false,
7872     
7873     getAutoCreate : function()
7874     {
7875         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7876         
7877         cfg = {
7878             tag: 'table',
7879             cls : 'table',
7880             cn : []
7881         };
7882         if (this.scrollBody) {
7883             cfg.cls += ' table-body-fixed';
7884         }    
7885         if (this.striped) {
7886             cfg.cls += ' table-striped';
7887         }
7888         
7889         if (this.hover) {
7890             cfg.cls += ' table-hover';
7891         }
7892         if (this.bordered) {
7893             cfg.cls += ' table-bordered';
7894         }
7895         if (this.condensed) {
7896             cfg.cls += ' table-condensed';
7897         }
7898         if (this.responsive) {
7899             cfg.cls += ' table-responsive';
7900         }
7901         
7902         if (this.cls) {
7903             cfg.cls+=  ' ' +this.cls;
7904         }
7905         
7906         // this lot should be simplifed...
7907         var _t = this;
7908         var cp = [
7909             'align',
7910             'bgcolor',
7911             'border',
7912             'cellpadding',
7913             'cellspacing',
7914             'frame',
7915             'rules',
7916             'sortable',
7917             'summary',
7918             'width'
7919         ].forEach(function(k) {
7920             if (_t[k]) {
7921                 cfg[k] = _t[k];
7922             }
7923         });
7924         
7925         
7926         if (this.layout) {
7927             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7928         }
7929         
7930         if(this.store || this.cm){
7931             if(this.headerShow){
7932                 cfg.cn.push(this.renderHeader());
7933             }
7934             
7935             cfg.cn.push(this.renderBody());
7936             
7937             if(this.footerShow){
7938                 cfg.cn.push(this.renderFooter());
7939             }
7940             // where does this come from?
7941             //cfg.cls+=  ' TableGrid';
7942         }
7943         
7944         return { cn : [ cfg ] };
7945     },
7946     
7947     initEvents : function()
7948     {   
7949         if(!this.store || !this.cm){
7950             return;
7951         }
7952         if (this.selModel) {
7953             this.selModel.initEvents();
7954         }
7955         
7956         
7957         //Roo.log('initEvents with ds!!!!');
7958         
7959         this.mainBody = this.el.select('tbody', true).first();
7960         this.mainHead = this.el.select('thead', true).first();
7961         this.mainFoot = this.el.select('tfoot', true).first();
7962         
7963         
7964         
7965         var _this = this;
7966         
7967         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7968             e.on('click', _this.sort, _this);
7969         });
7970         
7971         this.mainBody.on("click", this.onClick, this);
7972         this.mainBody.on("dblclick", this.onDblClick, this);
7973         
7974         // why is this done????? = it breaks dialogs??
7975         //this.parent().el.setStyle('position', 'relative');
7976         
7977         
7978         if (this.footer) {
7979             this.footer.parentId = this.id;
7980             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7981             
7982             if(this.lazyLoad){
7983                 this.el.select('tfoot tr td').first().addClass('hide');
7984             }
7985         } 
7986         
7987         if(this.loadMask) {
7988             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7989         }
7990         
7991         this.store.on('load', this.onLoad, this);
7992         this.store.on('beforeload', this.onBeforeLoad, this);
7993         this.store.on('update', this.onUpdate, this);
7994         this.store.on('add', this.onAdd, this);
7995         this.store.on("clear", this.clear, this);
7996         
7997         this.el.on("contextmenu", this.onContextMenu, this);
7998         
7999         this.mainBody.on('scroll', this.onBodyScroll, this);
8000         
8001         this.cm.on("headerchange", this.onHeaderChange, this);
8002         
8003         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8004         
8005     },
8006     
8007     onContextMenu : function(e, t)
8008     {
8009         this.processEvent("contextmenu", e);
8010     },
8011     
8012     processEvent : function(name, e)
8013     {
8014         if (name != 'touchstart' ) {
8015             this.fireEvent(name, e);    
8016         }
8017         
8018         var t = e.getTarget();
8019         
8020         var cell = Roo.get(t);
8021         
8022         if(!cell){
8023             return;
8024         }
8025         
8026         if(cell.findParent('tfoot', false, true)){
8027             return;
8028         }
8029         
8030         if(cell.findParent('thead', false, true)){
8031             
8032             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8033                 cell = Roo.get(t).findParent('th', false, true);
8034                 if (!cell) {
8035                     Roo.log("failed to find th in thead?");
8036                     Roo.log(e.getTarget());
8037                     return;
8038                 }
8039             }
8040             
8041             var cellIndex = cell.dom.cellIndex;
8042             
8043             var ename = name == 'touchstart' ? 'click' : name;
8044             this.fireEvent("header" + ename, this, cellIndex, e);
8045             
8046             return;
8047         }
8048         
8049         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8050             cell = Roo.get(t).findParent('td', false, true);
8051             if (!cell) {
8052                 Roo.log("failed to find th in tbody?");
8053                 Roo.log(e.getTarget());
8054                 return;
8055             }
8056         }
8057         
8058         var row = cell.findParent('tr', false, true);
8059         var cellIndex = cell.dom.cellIndex;
8060         var rowIndex = row.dom.rowIndex - 1;
8061         
8062         if(row !== false){
8063             
8064             this.fireEvent("row" + name, this, rowIndex, e);
8065             
8066             if(cell !== false){
8067             
8068                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8069             }
8070         }
8071         
8072     },
8073     
8074     onMouseover : function(e, el)
8075     {
8076         var cell = Roo.get(el);
8077         
8078         if(!cell){
8079             return;
8080         }
8081         
8082         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8083             cell = cell.findParent('td', false, true);
8084         }
8085         
8086         var row = cell.findParent('tr', false, true);
8087         var cellIndex = cell.dom.cellIndex;
8088         var rowIndex = row.dom.rowIndex - 1; // start from 0
8089         
8090         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8091         
8092     },
8093     
8094     onMouseout : function(e, el)
8095     {
8096         var cell = Roo.get(el);
8097         
8098         if(!cell){
8099             return;
8100         }
8101         
8102         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8103             cell = cell.findParent('td', false, true);
8104         }
8105         
8106         var row = cell.findParent('tr', false, true);
8107         var cellIndex = cell.dom.cellIndex;
8108         var rowIndex = row.dom.rowIndex - 1; // start from 0
8109         
8110         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8111         
8112     },
8113     
8114     onClick : function(e, el)
8115     {
8116         var cell = Roo.get(el);
8117         
8118         if(!cell || (!this.cellSelection && !this.rowSelection)){
8119             return;
8120         }
8121         
8122         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8123             cell = cell.findParent('td', false, true);
8124         }
8125         
8126         if(!cell || typeof(cell) == 'undefined'){
8127             return;
8128         }
8129         
8130         var row = cell.findParent('tr', false, true);
8131         
8132         if(!row || typeof(row) == 'undefined'){
8133             return;
8134         }
8135         
8136         var cellIndex = cell.dom.cellIndex;
8137         var rowIndex = this.getRowIndex(row);
8138         
8139         // why??? - should these not be based on SelectionModel?
8140         if(this.cellSelection){
8141             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8142         }
8143         
8144         if(this.rowSelection){
8145             this.fireEvent('rowclick', this, row, rowIndex, e);
8146         }
8147         
8148         
8149     },
8150         
8151     onDblClick : function(e,el)
8152     {
8153         var cell = Roo.get(el);
8154         
8155         if(!cell || (!this.cellSelection && !this.rowSelection)){
8156             return;
8157         }
8158         
8159         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8160             cell = cell.findParent('td', false, true);
8161         }
8162         
8163         if(!cell || typeof(cell) == 'undefined'){
8164             return;
8165         }
8166         
8167         var row = cell.findParent('tr', false, true);
8168         
8169         if(!row || typeof(row) == 'undefined'){
8170             return;
8171         }
8172         
8173         var cellIndex = cell.dom.cellIndex;
8174         var rowIndex = this.getRowIndex(row);
8175         
8176         if(this.cellSelection){
8177             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8178         }
8179         
8180         if(this.rowSelection){
8181             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8182         }
8183     },
8184     
8185     sort : function(e,el)
8186     {
8187         var col = Roo.get(el);
8188         
8189         if(!col.hasClass('sortable')){
8190             return;
8191         }
8192         
8193         var sort = col.attr('sort');
8194         var dir = 'ASC';
8195         
8196         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8197             dir = 'DESC';
8198         }
8199         
8200         this.store.sortInfo = {field : sort, direction : dir};
8201         
8202         if (this.footer) {
8203             Roo.log("calling footer first");
8204             this.footer.onClick('first');
8205         } else {
8206         
8207             this.store.load({ params : { start : 0 } });
8208         }
8209     },
8210     
8211     renderHeader : function()
8212     {
8213         var header = {
8214             tag: 'thead',
8215             cn : []
8216         };
8217         
8218         var cm = this.cm;
8219         this.totalWidth = 0;
8220         
8221         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8222             
8223             var config = cm.config[i];
8224             
8225             var c = {
8226                 tag: 'th',
8227                 cls : 'x-hcol-' + i,
8228                 style : '',
8229                 html: cm.getColumnHeader(i)
8230             };
8231             
8232             var hh = '';
8233             
8234             if(typeof(config.sortable) != 'undefined' && config.sortable){
8235                 c.cls = 'sortable';
8236                 c.html = '<i class="glyphicon"></i>' + c.html;
8237             }
8238             
8239             // could use BS4 hidden-..-down 
8240             
8241             if(typeof(config.lgHeader) != 'undefined'){
8242                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8243             }
8244             
8245             if(typeof(config.mdHeader) != 'undefined'){
8246                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8247             }
8248             
8249             if(typeof(config.smHeader) != 'undefined'){
8250                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8251             }
8252             
8253             if(typeof(config.xsHeader) != 'undefined'){
8254                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8255             }
8256             
8257             if(hh.length){
8258                 c.html = hh;
8259             }
8260             
8261             if(typeof(config.tooltip) != 'undefined'){
8262                 c.tooltip = config.tooltip;
8263             }
8264             
8265             if(typeof(config.colspan) != 'undefined'){
8266                 c.colspan = config.colspan;
8267             }
8268             
8269             if(typeof(config.hidden) != 'undefined' && config.hidden){
8270                 c.style += ' display:none;';
8271             }
8272             
8273             if(typeof(config.dataIndex) != 'undefined'){
8274                 c.sort = config.dataIndex;
8275             }
8276             
8277            
8278             
8279             if(typeof(config.align) != 'undefined' && config.align.length){
8280                 c.style += ' text-align:' + config.align + ';';
8281             }
8282             
8283             if(typeof(config.width) != 'undefined'){
8284                 c.style += ' width:' + config.width + 'px;';
8285                 this.totalWidth += config.width;
8286             } else {
8287                 this.totalWidth += 100; // assume minimum of 100 per column?
8288             }
8289             
8290             if(typeof(config.cls) != 'undefined'){
8291                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8292             }
8293             
8294             ['xs','sm','md','lg'].map(function(size){
8295                 
8296                 if(typeof(config[size]) == 'undefined'){
8297                     return;
8298                 }
8299                  
8300                 if (!config[size]) { // 0 = hidden
8301                     // BS 4 '0' is treated as hide that column and below.
8302                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8303                     return;
8304                 }
8305                 
8306                 c.cls += ' col-' + size + '-' + config[size] + (
8307                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8308                 );
8309                 
8310                 
8311             });
8312             
8313             header.cn.push(c)
8314         }
8315         
8316         return header;
8317     },
8318     
8319     renderBody : function()
8320     {
8321         var body = {
8322             tag: 'tbody',
8323             cn : [
8324                 {
8325                     tag: 'tr',
8326                     cn : [
8327                         {
8328                             tag : 'td',
8329                             colspan :  this.cm.getColumnCount()
8330                         }
8331                     ]
8332                 }
8333             ]
8334         };
8335         
8336         return body;
8337     },
8338     
8339     renderFooter : function()
8340     {
8341         var footer = {
8342             tag: 'tfoot',
8343             cn : [
8344                 {
8345                     tag: 'tr',
8346                     cn : [
8347                         {
8348                             tag : 'td',
8349                             colspan :  this.cm.getColumnCount()
8350                         }
8351                     ]
8352                 }
8353             ]
8354         };
8355         
8356         return footer;
8357     },
8358     
8359     
8360     
8361     onLoad : function()
8362     {
8363 //        Roo.log('ds onload');
8364         this.clear();
8365         
8366         var _this = this;
8367         var cm = this.cm;
8368         var ds = this.store;
8369         
8370         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8371             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8372             if (_this.store.sortInfo) {
8373                     
8374                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8375                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8376                 }
8377                 
8378                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8379                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8380                 }
8381             }
8382         });
8383         
8384         var tbody =  this.mainBody;
8385               
8386         if(ds.getCount() > 0){
8387             ds.data.each(function(d,rowIndex){
8388                 var row =  this.renderRow(cm, ds, rowIndex);
8389                 
8390                 tbody.createChild(row);
8391                 
8392                 var _this = this;
8393                 
8394                 if(row.cellObjects.length){
8395                     Roo.each(row.cellObjects, function(r){
8396                         _this.renderCellObject(r);
8397                     })
8398                 }
8399                 
8400             }, this);
8401         }
8402         
8403         var tfoot = this.el.select('tfoot', true).first();
8404         
8405         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8406             
8407             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8408             
8409             var total = this.ds.getTotalCount();
8410             
8411             if(this.footer.pageSize < total){
8412                 this.mainFoot.show();
8413             }
8414         }
8415         
8416         Roo.each(this.el.select('tbody td', true).elements, function(e){
8417             e.on('mouseover', _this.onMouseover, _this);
8418         });
8419         
8420         Roo.each(this.el.select('tbody td', true).elements, function(e){
8421             e.on('mouseout', _this.onMouseout, _this);
8422         });
8423         this.fireEvent('rowsrendered', this);
8424         
8425         this.autoSize();
8426     },
8427     
8428     
8429     onUpdate : function(ds,record)
8430     {
8431         this.refreshRow(record);
8432         this.autoSize();
8433     },
8434     
8435     onRemove : function(ds, record, index, isUpdate){
8436         if(isUpdate !== true){
8437             this.fireEvent("beforerowremoved", this, index, record);
8438         }
8439         var bt = this.mainBody.dom;
8440         
8441         var rows = this.el.select('tbody > tr', true).elements;
8442         
8443         if(typeof(rows[index]) != 'undefined'){
8444             bt.removeChild(rows[index].dom);
8445         }
8446         
8447 //        if(bt.rows[index]){
8448 //            bt.removeChild(bt.rows[index]);
8449 //        }
8450         
8451         if(isUpdate !== true){
8452             //this.stripeRows(index);
8453             //this.syncRowHeights(index, index);
8454             //this.layout();
8455             this.fireEvent("rowremoved", this, index, record);
8456         }
8457     },
8458     
8459     onAdd : function(ds, records, rowIndex)
8460     {
8461         //Roo.log('on Add called');
8462         // - note this does not handle multiple adding very well..
8463         var bt = this.mainBody.dom;
8464         for (var i =0 ; i < records.length;i++) {
8465             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8466             //Roo.log(records[i]);
8467             //Roo.log(this.store.getAt(rowIndex+i));
8468             this.insertRow(this.store, rowIndex + i, false);
8469             return;
8470         }
8471         
8472     },
8473     
8474     
8475     refreshRow : function(record){
8476         var ds = this.store, index;
8477         if(typeof record == 'number'){
8478             index = record;
8479             record = ds.getAt(index);
8480         }else{
8481             index = ds.indexOf(record);
8482             if (index < 0) {
8483                 return; // should not happen - but seems to 
8484             }
8485         }
8486         this.insertRow(ds, index, true);
8487         this.autoSize();
8488         this.onRemove(ds, record, index+1, true);
8489         this.autoSize();
8490         //this.syncRowHeights(index, index);
8491         //this.layout();
8492         this.fireEvent("rowupdated", this, index, record);
8493     },
8494     
8495     insertRow : function(dm, rowIndex, isUpdate){
8496         
8497         if(!isUpdate){
8498             this.fireEvent("beforerowsinserted", this, rowIndex);
8499         }
8500             //var s = this.getScrollState();
8501         var row = this.renderRow(this.cm, this.store, rowIndex);
8502         // insert before rowIndex..
8503         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8504         
8505         var _this = this;
8506                 
8507         if(row.cellObjects.length){
8508             Roo.each(row.cellObjects, function(r){
8509                 _this.renderCellObject(r);
8510             })
8511         }
8512             
8513         if(!isUpdate){
8514             this.fireEvent("rowsinserted", this, rowIndex);
8515             //this.syncRowHeights(firstRow, lastRow);
8516             //this.stripeRows(firstRow);
8517             //this.layout();
8518         }
8519         
8520     },
8521     
8522     
8523     getRowDom : function(rowIndex)
8524     {
8525         var rows = this.el.select('tbody > tr', true).elements;
8526         
8527         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8528         
8529     },
8530     // returns the object tree for a tr..
8531   
8532     
8533     renderRow : function(cm, ds, rowIndex) 
8534     {
8535         var d = ds.getAt(rowIndex);
8536         
8537         var row = {
8538             tag : 'tr',
8539             cls : 'x-row-' + rowIndex,
8540             cn : []
8541         };
8542             
8543         var cellObjects = [];
8544         
8545         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8546             var config = cm.config[i];
8547             
8548             var renderer = cm.getRenderer(i);
8549             var value = '';
8550             var id = false;
8551             
8552             if(typeof(renderer) !== 'undefined'){
8553                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8554             }
8555             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8556             // and are rendered into the cells after the row is rendered - using the id for the element.
8557             
8558             if(typeof(value) === 'object'){
8559                 id = Roo.id();
8560                 cellObjects.push({
8561                     container : id,
8562                     cfg : value 
8563                 })
8564             }
8565             
8566             var rowcfg = {
8567                 record: d,
8568                 rowIndex : rowIndex,
8569                 colIndex : i,
8570                 rowClass : ''
8571             };
8572
8573             this.fireEvent('rowclass', this, rowcfg);
8574             
8575             var td = {
8576                 tag: 'td',
8577                 cls : rowcfg.rowClass + ' x-col-' + i,
8578                 style: '',
8579                 html: (typeof(value) === 'object') ? '' : value
8580             };
8581             
8582             if (id) {
8583                 td.id = id;
8584             }
8585             
8586             if(typeof(config.colspan) != 'undefined'){
8587                 td.colspan = config.colspan;
8588             }
8589             
8590             if(typeof(config.hidden) != 'undefined' && config.hidden){
8591                 td.style += ' display:none;';
8592             }
8593             
8594             if(typeof(config.align) != 'undefined' && config.align.length){
8595                 td.style += ' text-align:' + config.align + ';';
8596             }
8597             if(typeof(config.valign) != 'undefined' && config.valign.length){
8598                 td.style += ' vertical-align:' + config.valign + ';';
8599             }
8600             
8601             if(typeof(config.width) != 'undefined'){
8602                 td.style += ' width:' +  config.width + 'px;';
8603             }
8604             
8605             if(typeof(config.cursor) != 'undefined'){
8606                 td.style += ' cursor:' +  config.cursor + ';';
8607             }
8608             
8609             if(typeof(config.cls) != 'undefined'){
8610                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8611             }
8612             
8613             ['xs','sm','md','lg'].map(function(size){
8614                 
8615                 if(typeof(config[size]) == 'undefined'){
8616                     return;
8617                 }
8618                 
8619                 
8620                   
8621                 if (!config[size]) { // 0 = hidden
8622                     // BS 4 '0' is treated as hide that column and below.
8623                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8624                     return;
8625                 }
8626                 
8627                 td.cls += ' col-' + size + '-' + config[size] + (
8628                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8629                 );
8630                  
8631
8632             });
8633             
8634             row.cn.push(td);
8635            
8636         }
8637         
8638         row.cellObjects = cellObjects;
8639         
8640         return row;
8641           
8642     },
8643     
8644     
8645     
8646     onBeforeLoad : function()
8647     {
8648         
8649     },
8650      /**
8651      * Remove all rows
8652      */
8653     clear : function()
8654     {
8655         this.el.select('tbody', true).first().dom.innerHTML = '';
8656     },
8657     /**
8658      * Show or hide a row.
8659      * @param {Number} rowIndex to show or hide
8660      * @param {Boolean} state hide
8661      */
8662     setRowVisibility : function(rowIndex, state)
8663     {
8664         var bt = this.mainBody.dom;
8665         
8666         var rows = this.el.select('tbody > tr', true).elements;
8667         
8668         if(typeof(rows[rowIndex]) == 'undefined'){
8669             return;
8670         }
8671         rows[rowIndex].dom.style.display = state ? '' : 'none';
8672     },
8673     
8674     
8675     getSelectionModel : function(){
8676         if(!this.selModel){
8677             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8678         }
8679         return this.selModel;
8680     },
8681     /*
8682      * Render the Roo.bootstrap object from renderder
8683      */
8684     renderCellObject : function(r)
8685     {
8686         var _this = this;
8687         
8688         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8689         
8690         var t = r.cfg.render(r.container);
8691         
8692         if(r.cfg.cn){
8693             Roo.each(r.cfg.cn, function(c){
8694                 var child = {
8695                     container: t.getChildContainer(),
8696                     cfg: c
8697                 };
8698                 _this.renderCellObject(child);
8699             })
8700         }
8701     },
8702     
8703     getRowIndex : function(row)
8704     {
8705         var rowIndex = -1;
8706         
8707         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8708             if(el != row){
8709                 return;
8710             }
8711             
8712             rowIndex = index;
8713         });
8714         
8715         return rowIndex;
8716     },
8717      /**
8718      * Returns the grid's underlying element = used by panel.Grid
8719      * @return {Element} The element
8720      */
8721     getGridEl : function(){
8722         return this.el;
8723     },
8724      /**
8725      * Forces a resize - used by panel.Grid
8726      * @return {Element} The element
8727      */
8728     autoSize : function()
8729     {
8730         //var ctr = Roo.get(this.container.dom.parentElement);
8731         var ctr = Roo.get(this.el.dom);
8732         
8733         var thd = this.getGridEl().select('thead',true).first();
8734         var tbd = this.getGridEl().select('tbody', true).first();
8735         var tfd = this.getGridEl().select('tfoot', true).first();
8736         
8737         var cw = ctr.getWidth();
8738         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8739         
8740         if (tbd) {
8741             
8742             tbd.setWidth(ctr.getWidth());
8743             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8744             // this needs fixing for various usage - currently only hydra job advers I think..
8745             //tdb.setHeight(
8746             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8747             //); 
8748             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8749             cw -= barsize;
8750         }
8751         cw = Math.max(cw, this.totalWidth);
8752         this.getGridEl().select('tbody tr',true).setWidth(cw);
8753         
8754         // resize 'expandable coloumn?
8755         
8756         return; // we doe not have a view in this design..
8757         
8758     },
8759     onBodyScroll: function()
8760     {
8761         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8762         if(this.mainHead){
8763             this.mainHead.setStyle({
8764                 'position' : 'relative',
8765                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8766             });
8767         }
8768         
8769         if(this.lazyLoad){
8770             
8771             var scrollHeight = this.mainBody.dom.scrollHeight;
8772             
8773             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8774             
8775             var height = this.mainBody.getHeight();
8776             
8777             if(scrollHeight - height == scrollTop) {
8778                 
8779                 var total = this.ds.getTotalCount();
8780                 
8781                 if(this.footer.cursor + this.footer.pageSize < total){
8782                     
8783                     this.footer.ds.load({
8784                         params : {
8785                             start : this.footer.cursor + this.footer.pageSize,
8786                             limit : this.footer.pageSize
8787                         },
8788                         add : true
8789                     });
8790                 }
8791             }
8792             
8793         }
8794     },
8795     
8796     onHeaderChange : function()
8797     {
8798         var header = this.renderHeader();
8799         var table = this.el.select('table', true).first();
8800         
8801         this.mainHead.remove();
8802         this.mainHead = table.createChild(header, this.mainBody, false);
8803     },
8804     
8805     onHiddenChange : function(colModel, colIndex, hidden)
8806     {
8807         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8808         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8809         
8810         this.CSS.updateRule(thSelector, "display", "");
8811         this.CSS.updateRule(tdSelector, "display", "");
8812         
8813         if(hidden){
8814             this.CSS.updateRule(thSelector, "display", "none");
8815             this.CSS.updateRule(tdSelector, "display", "none");
8816         }
8817         
8818         this.onHeaderChange();
8819         this.onLoad();
8820     },
8821     
8822     setColumnWidth: function(col_index, width)
8823     {
8824         // width = "md-2 xs-2..."
8825         if(!this.colModel.config[col_index]) {
8826             return;
8827         }
8828         
8829         var w = width.split(" ");
8830         
8831         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8832         
8833         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8834         
8835         
8836         for(var j = 0; j < w.length; j++) {
8837             
8838             if(!w[j]) {
8839                 continue;
8840             }
8841             
8842             var size_cls = w[j].split("-");
8843             
8844             if(!Number.isInteger(size_cls[1] * 1)) {
8845                 continue;
8846             }
8847             
8848             if(!this.colModel.config[col_index][size_cls[0]]) {
8849                 continue;
8850             }
8851             
8852             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8853                 continue;
8854             }
8855             
8856             h_row[0].classList.replace(
8857                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8858                 "col-"+size_cls[0]+"-"+size_cls[1]
8859             );
8860             
8861             for(var i = 0; i < rows.length; i++) {
8862                 
8863                 var size_cls = w[j].split("-");
8864                 
8865                 if(!Number.isInteger(size_cls[1] * 1)) {
8866                     continue;
8867                 }
8868                 
8869                 if(!this.colModel.config[col_index][size_cls[0]]) {
8870                     continue;
8871                 }
8872                 
8873                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8874                     continue;
8875                 }
8876                 
8877                 rows[i].classList.replace(
8878                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8879                     "col-"+size_cls[0]+"-"+size_cls[1]
8880                 );
8881             }
8882             
8883             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8884         }
8885     }
8886 });
8887
8888  
8889
8890  /*
8891  * - LGPL
8892  *
8893  * table cell
8894  * 
8895  */
8896
8897 /**
8898  * @class Roo.bootstrap.TableCell
8899  * @extends Roo.bootstrap.Component
8900  * Bootstrap TableCell class
8901  * @cfg {String} html cell contain text
8902  * @cfg {String} cls cell class
8903  * @cfg {String} tag cell tag (td|th) default td
8904  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8905  * @cfg {String} align Aligns the content in a cell
8906  * @cfg {String} axis Categorizes cells
8907  * @cfg {String} bgcolor Specifies the background color of a cell
8908  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8909  * @cfg {Number} colspan Specifies the number of columns a cell should span
8910  * @cfg {String} headers Specifies one or more header cells a cell is related to
8911  * @cfg {Number} height Sets the height of a cell
8912  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8913  * @cfg {Number} rowspan Sets the number of rows a cell should span
8914  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8915  * @cfg {String} valign Vertical aligns the content in a cell
8916  * @cfg {Number} width Specifies the width of a cell
8917  * 
8918  * @constructor
8919  * Create a new TableCell
8920  * @param {Object} config The config object
8921  */
8922
8923 Roo.bootstrap.TableCell = function(config){
8924     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8925 };
8926
8927 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8928     
8929     html: false,
8930     cls: false,
8931     tag: false,
8932     abbr: false,
8933     align: false,
8934     axis: false,
8935     bgcolor: false,
8936     charoff: false,
8937     colspan: false,
8938     headers: false,
8939     height: false,
8940     nowrap: false,
8941     rowspan: false,
8942     scope: false,
8943     valign: false,
8944     width: false,
8945     
8946     
8947     getAutoCreate : function(){
8948         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8949         
8950         cfg = {
8951             tag: 'td'
8952         };
8953         
8954         if(this.tag){
8955             cfg.tag = this.tag;
8956         }
8957         
8958         if (this.html) {
8959             cfg.html=this.html
8960         }
8961         if (this.cls) {
8962             cfg.cls=this.cls
8963         }
8964         if (this.abbr) {
8965             cfg.abbr=this.abbr
8966         }
8967         if (this.align) {
8968             cfg.align=this.align
8969         }
8970         if (this.axis) {
8971             cfg.axis=this.axis
8972         }
8973         if (this.bgcolor) {
8974             cfg.bgcolor=this.bgcolor
8975         }
8976         if (this.charoff) {
8977             cfg.charoff=this.charoff
8978         }
8979         if (this.colspan) {
8980             cfg.colspan=this.colspan
8981         }
8982         if (this.headers) {
8983             cfg.headers=this.headers
8984         }
8985         if (this.height) {
8986             cfg.height=this.height
8987         }
8988         if (this.nowrap) {
8989             cfg.nowrap=this.nowrap
8990         }
8991         if (this.rowspan) {
8992             cfg.rowspan=this.rowspan
8993         }
8994         if (this.scope) {
8995             cfg.scope=this.scope
8996         }
8997         if (this.valign) {
8998             cfg.valign=this.valign
8999         }
9000         if (this.width) {
9001             cfg.width=this.width
9002         }
9003         
9004         
9005         return cfg;
9006     }
9007    
9008 });
9009
9010  
9011
9012  /*
9013  * - LGPL
9014  *
9015  * table row
9016  * 
9017  */
9018
9019 /**
9020  * @class Roo.bootstrap.TableRow
9021  * @extends Roo.bootstrap.Component
9022  * Bootstrap TableRow class
9023  * @cfg {String} cls row class
9024  * @cfg {String} align Aligns the content in a table row
9025  * @cfg {String} bgcolor Specifies a background color for a table row
9026  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9027  * @cfg {String} valign Vertical aligns the content in a table row
9028  * 
9029  * @constructor
9030  * Create a new TableRow
9031  * @param {Object} config The config object
9032  */
9033
9034 Roo.bootstrap.TableRow = function(config){
9035     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9036 };
9037
9038 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9039     
9040     cls: false,
9041     align: false,
9042     bgcolor: false,
9043     charoff: false,
9044     valign: false,
9045     
9046     getAutoCreate : function(){
9047         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9048         
9049         cfg = {
9050             tag: 'tr'
9051         };
9052             
9053         if(this.cls){
9054             cfg.cls = this.cls;
9055         }
9056         if(this.align){
9057             cfg.align = this.align;
9058         }
9059         if(this.bgcolor){
9060             cfg.bgcolor = this.bgcolor;
9061         }
9062         if(this.charoff){
9063             cfg.charoff = this.charoff;
9064         }
9065         if(this.valign){
9066             cfg.valign = this.valign;
9067         }
9068         
9069         return cfg;
9070     }
9071    
9072 });
9073
9074  
9075
9076  /*
9077  * - LGPL
9078  *
9079  * table body
9080  * 
9081  */
9082
9083 /**
9084  * @class Roo.bootstrap.TableBody
9085  * @extends Roo.bootstrap.Component
9086  * Bootstrap TableBody class
9087  * @cfg {String} cls element class
9088  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9089  * @cfg {String} align Aligns the content inside the element
9090  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9091  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9092  * 
9093  * @constructor
9094  * Create a new TableBody
9095  * @param {Object} config The config object
9096  */
9097
9098 Roo.bootstrap.TableBody = function(config){
9099     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9100 };
9101
9102 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9103     
9104     cls: false,
9105     tag: false,
9106     align: false,
9107     charoff: false,
9108     valign: false,
9109     
9110     getAutoCreate : function(){
9111         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9112         
9113         cfg = {
9114             tag: 'tbody'
9115         };
9116             
9117         if (this.cls) {
9118             cfg.cls=this.cls
9119         }
9120         if(this.tag){
9121             cfg.tag = this.tag;
9122         }
9123         
9124         if(this.align){
9125             cfg.align = this.align;
9126         }
9127         if(this.charoff){
9128             cfg.charoff = this.charoff;
9129         }
9130         if(this.valign){
9131             cfg.valign = this.valign;
9132         }
9133         
9134         return cfg;
9135     }
9136     
9137     
9138 //    initEvents : function()
9139 //    {
9140 //        
9141 //        if(!this.store){
9142 //            return;
9143 //        }
9144 //        
9145 //        this.store = Roo.factory(this.store, Roo.data);
9146 //        this.store.on('load', this.onLoad, this);
9147 //        
9148 //        this.store.load();
9149 //        
9150 //    },
9151 //    
9152 //    onLoad: function () 
9153 //    {   
9154 //        this.fireEvent('load', this);
9155 //    }
9156 //    
9157 //   
9158 });
9159
9160  
9161
9162  /*
9163  * Based on:
9164  * Ext JS Library 1.1.1
9165  * Copyright(c) 2006-2007, Ext JS, LLC.
9166  *
9167  * Originally Released Under LGPL - original licence link has changed is not relivant.
9168  *
9169  * Fork - LGPL
9170  * <script type="text/javascript">
9171  */
9172
9173 // as we use this in bootstrap.
9174 Roo.namespace('Roo.form');
9175  /**
9176  * @class Roo.form.Action
9177  * Internal Class used to handle form actions
9178  * @constructor
9179  * @param {Roo.form.BasicForm} el The form element or its id
9180  * @param {Object} config Configuration options
9181  */
9182
9183  
9184  
9185 // define the action interface
9186 Roo.form.Action = function(form, options){
9187     this.form = form;
9188     this.options = options || {};
9189 };
9190 /**
9191  * Client Validation Failed
9192  * @const 
9193  */
9194 Roo.form.Action.CLIENT_INVALID = 'client';
9195 /**
9196  * Server Validation Failed
9197  * @const 
9198  */
9199 Roo.form.Action.SERVER_INVALID = 'server';
9200  /**
9201  * Connect to Server Failed
9202  * @const 
9203  */
9204 Roo.form.Action.CONNECT_FAILURE = 'connect';
9205 /**
9206  * Reading Data from Server Failed
9207  * @const 
9208  */
9209 Roo.form.Action.LOAD_FAILURE = 'load';
9210
9211 Roo.form.Action.prototype = {
9212     type : 'default',
9213     failureType : undefined,
9214     response : undefined,
9215     result : undefined,
9216
9217     // interface method
9218     run : function(options){
9219
9220     },
9221
9222     // interface method
9223     success : function(response){
9224
9225     },
9226
9227     // interface method
9228     handleResponse : function(response){
9229
9230     },
9231
9232     // default connection failure
9233     failure : function(response){
9234         
9235         this.response = response;
9236         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9237         this.form.afterAction(this, false);
9238     },
9239
9240     processResponse : function(response){
9241         this.response = response;
9242         if(!response.responseText){
9243             return true;
9244         }
9245         this.result = this.handleResponse(response);
9246         return this.result;
9247     },
9248
9249     // utility functions used internally
9250     getUrl : function(appendParams){
9251         var url = this.options.url || this.form.url || this.form.el.dom.action;
9252         if(appendParams){
9253             var p = this.getParams();
9254             if(p){
9255                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9256             }
9257         }
9258         return url;
9259     },
9260
9261     getMethod : function(){
9262         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9263     },
9264
9265     getParams : function(){
9266         var bp = this.form.baseParams;
9267         var p = this.options.params;
9268         if(p){
9269             if(typeof p == "object"){
9270                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9271             }else if(typeof p == 'string' && bp){
9272                 p += '&' + Roo.urlEncode(bp);
9273             }
9274         }else if(bp){
9275             p = Roo.urlEncode(bp);
9276         }
9277         return p;
9278     },
9279
9280     createCallback : function(){
9281         return {
9282             success: this.success,
9283             failure: this.failure,
9284             scope: this,
9285             timeout: (this.form.timeout*1000),
9286             upload: this.form.fileUpload ? this.success : undefined
9287         };
9288     }
9289 };
9290
9291 Roo.form.Action.Submit = function(form, options){
9292     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9293 };
9294
9295 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9296     type : 'submit',
9297
9298     haveProgress : false,
9299     uploadComplete : false,
9300     
9301     // uploadProgress indicator.
9302     uploadProgress : function()
9303     {
9304         if (!this.form.progressUrl) {
9305             return;
9306         }
9307         
9308         if (!this.haveProgress) {
9309             Roo.MessageBox.progress("Uploading", "Uploading");
9310         }
9311         if (this.uploadComplete) {
9312            Roo.MessageBox.hide();
9313            return;
9314         }
9315         
9316         this.haveProgress = true;
9317    
9318         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9319         
9320         var c = new Roo.data.Connection();
9321         c.request({
9322             url : this.form.progressUrl,
9323             params: {
9324                 id : uid
9325             },
9326             method: 'GET',
9327             success : function(req){
9328                //console.log(data);
9329                 var rdata = false;
9330                 var edata;
9331                 try  {
9332                    rdata = Roo.decode(req.responseText)
9333                 } catch (e) {
9334                     Roo.log("Invalid data from server..");
9335                     Roo.log(edata);
9336                     return;
9337                 }
9338                 if (!rdata || !rdata.success) {
9339                     Roo.log(rdata);
9340                     Roo.MessageBox.alert(Roo.encode(rdata));
9341                     return;
9342                 }
9343                 var data = rdata.data;
9344                 
9345                 if (this.uploadComplete) {
9346                    Roo.MessageBox.hide();
9347                    return;
9348                 }
9349                    
9350                 if (data){
9351                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9352                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9353                     );
9354                 }
9355                 this.uploadProgress.defer(2000,this);
9356             },
9357        
9358             failure: function(data) {
9359                 Roo.log('progress url failed ');
9360                 Roo.log(data);
9361             },
9362             scope : this
9363         });
9364            
9365     },
9366     
9367     
9368     run : function()
9369     {
9370         // run get Values on the form, so it syncs any secondary forms.
9371         this.form.getValues();
9372         
9373         var o = this.options;
9374         var method = this.getMethod();
9375         var isPost = method == 'POST';
9376         if(o.clientValidation === false || this.form.isValid()){
9377             
9378             if (this.form.progressUrl) {
9379                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9380                     (new Date() * 1) + '' + Math.random());
9381                     
9382             } 
9383             
9384             
9385             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9386                 form:this.form.el.dom,
9387                 url:this.getUrl(!isPost),
9388                 method: method,
9389                 params:isPost ? this.getParams() : null,
9390                 isUpload: this.form.fileUpload,
9391                 formData : this.form.formData
9392             }));
9393             
9394             this.uploadProgress();
9395
9396         }else if (o.clientValidation !== false){ // client validation failed
9397             this.failureType = Roo.form.Action.CLIENT_INVALID;
9398             this.form.afterAction(this, false);
9399         }
9400     },
9401
9402     success : function(response)
9403     {
9404         this.uploadComplete= true;
9405         if (this.haveProgress) {
9406             Roo.MessageBox.hide();
9407         }
9408         
9409         
9410         var result = this.processResponse(response);
9411         if(result === true || result.success){
9412             this.form.afterAction(this, true);
9413             return;
9414         }
9415         if(result.errors){
9416             this.form.markInvalid(result.errors);
9417             this.failureType = Roo.form.Action.SERVER_INVALID;
9418         }
9419         this.form.afterAction(this, false);
9420     },
9421     failure : function(response)
9422     {
9423         this.uploadComplete= true;
9424         if (this.haveProgress) {
9425             Roo.MessageBox.hide();
9426         }
9427         
9428         this.response = response;
9429         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9430         this.form.afterAction(this, false);
9431     },
9432     
9433     handleResponse : function(response){
9434         if(this.form.errorReader){
9435             var rs = this.form.errorReader.read(response);
9436             var errors = [];
9437             if(rs.records){
9438                 for(var i = 0, len = rs.records.length; i < len; i++) {
9439                     var r = rs.records[i];
9440                     errors[i] = r.data;
9441                 }
9442             }
9443             if(errors.length < 1){
9444                 errors = null;
9445             }
9446             return {
9447                 success : rs.success,
9448                 errors : errors
9449             };
9450         }
9451         var ret = false;
9452         try {
9453             ret = Roo.decode(response.responseText);
9454         } catch (e) {
9455             ret = {
9456                 success: false,
9457                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9458                 errors : []
9459             };
9460         }
9461         return ret;
9462         
9463     }
9464 });
9465
9466
9467 Roo.form.Action.Load = function(form, options){
9468     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9469     this.reader = this.form.reader;
9470 };
9471
9472 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9473     type : 'load',
9474
9475     run : function(){
9476         
9477         Roo.Ajax.request(Roo.apply(
9478                 this.createCallback(), {
9479                     method:this.getMethod(),
9480                     url:this.getUrl(false),
9481                     params:this.getParams()
9482         }));
9483     },
9484
9485     success : function(response){
9486         
9487         var result = this.processResponse(response);
9488         if(result === true || !result.success || !result.data){
9489             this.failureType = Roo.form.Action.LOAD_FAILURE;
9490             this.form.afterAction(this, false);
9491             return;
9492         }
9493         this.form.clearInvalid();
9494         this.form.setValues(result.data);
9495         this.form.afterAction(this, true);
9496     },
9497
9498     handleResponse : function(response){
9499         if(this.form.reader){
9500             var rs = this.form.reader.read(response);
9501             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9502             return {
9503                 success : rs.success,
9504                 data : data
9505             };
9506         }
9507         return Roo.decode(response.responseText);
9508     }
9509 });
9510
9511 Roo.form.Action.ACTION_TYPES = {
9512     'load' : Roo.form.Action.Load,
9513     'submit' : Roo.form.Action.Submit
9514 };/*
9515  * - LGPL
9516  *
9517  * form
9518  *
9519  */
9520
9521 /**
9522  * @class Roo.bootstrap.Form
9523  * @extends Roo.bootstrap.Component
9524  * Bootstrap Form class
9525  * @cfg {String} method  GET | POST (default POST)
9526  * @cfg {String} labelAlign top | left (default top)
9527  * @cfg {String} align left  | right - for navbars
9528  * @cfg {Boolean} loadMask load mask when submit (default true)
9529
9530  *
9531  * @constructor
9532  * Create a new Form
9533  * @param {Object} config The config object
9534  */
9535
9536
9537 Roo.bootstrap.Form = function(config){
9538     
9539     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9540     
9541     Roo.bootstrap.Form.popover.apply();
9542     
9543     this.addEvents({
9544         /**
9545          * @event clientvalidation
9546          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9547          * @param {Form} this
9548          * @param {Boolean} valid true if the form has passed client-side validation
9549          */
9550         clientvalidation: true,
9551         /**
9552          * @event beforeaction
9553          * Fires before any action is performed. Return false to cancel the action.
9554          * @param {Form} this
9555          * @param {Action} action The action to be performed
9556          */
9557         beforeaction: true,
9558         /**
9559          * @event actionfailed
9560          * Fires when an action fails.
9561          * @param {Form} this
9562          * @param {Action} action The action that failed
9563          */
9564         actionfailed : true,
9565         /**
9566          * @event actioncomplete
9567          * Fires when an action is completed.
9568          * @param {Form} this
9569          * @param {Action} action The action that completed
9570          */
9571         actioncomplete : true
9572     });
9573 };
9574
9575 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9576
9577      /**
9578      * @cfg {String} method
9579      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9580      */
9581     method : 'POST',
9582     /**
9583      * @cfg {String} url
9584      * The URL to use for form actions if one isn't supplied in the action options.
9585      */
9586     /**
9587      * @cfg {Boolean} fileUpload
9588      * Set to true if this form is a file upload.
9589      */
9590
9591     /**
9592      * @cfg {Object} baseParams
9593      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9594      */
9595
9596     /**
9597      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9598      */
9599     timeout: 30,
9600     /**
9601      * @cfg {Sting} align (left|right) for navbar forms
9602      */
9603     align : 'left',
9604
9605     // private
9606     activeAction : null,
9607
9608     /**
9609      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9610      * element by passing it or its id or mask the form itself by passing in true.
9611      * @type Mixed
9612      */
9613     waitMsgTarget : false,
9614
9615     loadMask : true,
9616     
9617     /**
9618      * @cfg {Boolean} errorMask (true|false) default false
9619      */
9620     errorMask : false,
9621     
9622     /**
9623      * @cfg {Number} maskOffset Default 100
9624      */
9625     maskOffset : 100,
9626     
9627     /**
9628      * @cfg {Boolean} maskBody
9629      */
9630     maskBody : false,
9631
9632     getAutoCreate : function(){
9633
9634         var cfg = {
9635             tag: 'form',
9636             method : this.method || 'POST',
9637             id : this.id || Roo.id(),
9638             cls : ''
9639         };
9640         if (this.parent().xtype.match(/^Nav/)) {
9641             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9642
9643         }
9644
9645         if (this.labelAlign == 'left' ) {
9646             cfg.cls += ' form-horizontal';
9647         }
9648
9649
9650         return cfg;
9651     },
9652     initEvents : function()
9653     {
9654         this.el.on('submit', this.onSubmit, this);
9655         // this was added as random key presses on the form where triggering form submit.
9656         this.el.on('keypress', function(e) {
9657             if (e.getCharCode() != 13) {
9658                 return true;
9659             }
9660             // we might need to allow it for textareas.. and some other items.
9661             // check e.getTarget().
9662
9663             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9664                 return true;
9665             }
9666
9667             Roo.log("keypress blocked");
9668
9669             e.preventDefault();
9670             return false;
9671         });
9672         
9673     },
9674     // private
9675     onSubmit : function(e){
9676         e.stopEvent();
9677     },
9678
9679      /**
9680      * Returns true if client-side validation on the form is successful.
9681      * @return Boolean
9682      */
9683     isValid : function(){
9684         var items = this.getItems();
9685         var valid = true;
9686         var target = false;
9687         
9688         items.each(function(f){
9689             
9690             if(f.validate()){
9691                 return;
9692             }
9693             
9694             Roo.log('invalid field: ' + f.name);
9695             
9696             valid = false;
9697
9698             if(!target && f.el.isVisible(true)){
9699                 target = f;
9700             }
9701            
9702         });
9703         
9704         if(this.errorMask && !valid){
9705             Roo.bootstrap.Form.popover.mask(this, target);
9706         }
9707         
9708         return valid;
9709     },
9710     
9711     /**
9712      * Returns true if any fields in this form have changed since their original load.
9713      * @return Boolean
9714      */
9715     isDirty : function(){
9716         var dirty = false;
9717         var items = this.getItems();
9718         items.each(function(f){
9719            if(f.isDirty()){
9720                dirty = true;
9721                return false;
9722            }
9723            return true;
9724         });
9725         return dirty;
9726     },
9727      /**
9728      * Performs a predefined action (submit or load) or custom actions you define on this form.
9729      * @param {String} actionName The name of the action type
9730      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9731      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9732      * accept other config options):
9733      * <pre>
9734 Property          Type             Description
9735 ----------------  ---------------  ----------------------------------------------------------------------------------
9736 url               String           The url for the action (defaults to the form's url)
9737 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9738 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9739 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9740                                    validate the form on the client (defaults to false)
9741      * </pre>
9742      * @return {BasicForm} this
9743      */
9744     doAction : function(action, options){
9745         if(typeof action == 'string'){
9746             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9747         }
9748         if(this.fireEvent('beforeaction', this, action) !== false){
9749             this.beforeAction(action);
9750             action.run.defer(100, action);
9751         }
9752         return this;
9753     },
9754
9755     // private
9756     beforeAction : function(action){
9757         var o = action.options;
9758         
9759         if(this.loadMask){
9760             
9761             if(this.maskBody){
9762                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9763             } else {
9764                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9765             }
9766         }
9767         // not really supported yet.. ??
9768
9769         //if(this.waitMsgTarget === true){
9770         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9771         //}else if(this.waitMsgTarget){
9772         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9773         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774         //}else {
9775         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9776        // }
9777
9778     },
9779
9780     // private
9781     afterAction : function(action, success){
9782         this.activeAction = null;
9783         var o = action.options;
9784
9785         if(this.loadMask){
9786             
9787             if(this.maskBody){
9788                 Roo.get(document.body).unmask();
9789             } else {
9790                 this.el.unmask();
9791             }
9792         }
9793         
9794         //if(this.waitMsgTarget === true){
9795 //            this.el.unmask();
9796         //}else if(this.waitMsgTarget){
9797         //    this.waitMsgTarget.unmask();
9798         //}else{
9799         //    Roo.MessageBox.updateProgress(1);
9800         //    Roo.MessageBox.hide();
9801        // }
9802         //
9803         if(success){
9804             if(o.reset){
9805                 this.reset();
9806             }
9807             Roo.callback(o.success, o.scope, [this, action]);
9808             this.fireEvent('actioncomplete', this, action);
9809
9810         }else{
9811
9812             // failure condition..
9813             // we have a scenario where updates need confirming.
9814             // eg. if a locking scenario exists..
9815             // we look for { errors : { needs_confirm : true }} in the response.
9816             if (
9817                 (typeof(action.result) != 'undefined')  &&
9818                 (typeof(action.result.errors) != 'undefined')  &&
9819                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9820            ){
9821                 var _t = this;
9822                 Roo.log("not supported yet");
9823                  /*
9824
9825                 Roo.MessageBox.confirm(
9826                     "Change requires confirmation",
9827                     action.result.errorMsg,
9828                     function(r) {
9829                         if (r != 'yes') {
9830                             return;
9831                         }
9832                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9833                     }
9834
9835                 );
9836                 */
9837
9838
9839                 return;
9840             }
9841
9842             Roo.callback(o.failure, o.scope, [this, action]);
9843             // show an error message if no failed handler is set..
9844             if (!this.hasListener('actionfailed')) {
9845                 Roo.log("need to add dialog support");
9846                 /*
9847                 Roo.MessageBox.alert("Error",
9848                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9849                         action.result.errorMsg :
9850                         "Saving Failed, please check your entries or try again"
9851                 );
9852                 */
9853             }
9854
9855             this.fireEvent('actionfailed', this, action);
9856         }
9857
9858     },
9859     /**
9860      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9861      * @param {String} id The value to search for
9862      * @return Field
9863      */
9864     findField : function(id){
9865         var items = this.getItems();
9866         var field = items.get(id);
9867         if(!field){
9868              items.each(function(f){
9869                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9870                     field = f;
9871                     return false;
9872                 }
9873                 return true;
9874             });
9875         }
9876         return field || null;
9877     },
9878      /**
9879      * Mark fields in this form invalid in bulk.
9880      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9881      * @return {BasicForm} this
9882      */
9883     markInvalid : function(errors){
9884         if(errors instanceof Array){
9885             for(var i = 0, len = errors.length; i < len; i++){
9886                 var fieldError = errors[i];
9887                 var f = this.findField(fieldError.id);
9888                 if(f){
9889                     f.markInvalid(fieldError.msg);
9890                 }
9891             }
9892         }else{
9893             var field, id;
9894             for(id in errors){
9895                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9896                     field.markInvalid(errors[id]);
9897                 }
9898             }
9899         }
9900         //Roo.each(this.childForms || [], function (f) {
9901         //    f.markInvalid(errors);
9902         //});
9903
9904         return this;
9905     },
9906
9907     /**
9908      * Set values for fields in this form in bulk.
9909      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9910      * @return {BasicForm} this
9911      */
9912     setValues : function(values){
9913         if(values instanceof Array){ // array of objects
9914             for(var i = 0, len = values.length; i < len; i++){
9915                 var v = values[i];
9916                 var f = this.findField(v.id);
9917                 if(f){
9918                     f.setValue(v.value);
9919                     if(this.trackResetOnLoad){
9920                         f.originalValue = f.getValue();
9921                     }
9922                 }
9923             }
9924         }else{ // object hash
9925             var field, id;
9926             for(id in values){
9927                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9928
9929                     if (field.setFromData &&
9930                         field.valueField &&
9931                         field.displayField &&
9932                         // combos' with local stores can
9933                         // be queried via setValue()
9934                         // to set their value..
9935                         (field.store && !field.store.isLocal)
9936                         ) {
9937                         // it's a combo
9938                         var sd = { };
9939                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9940                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9941                         field.setFromData(sd);
9942
9943                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9944                         
9945                         field.setFromData(values);
9946                         
9947                     } else {
9948                         field.setValue(values[id]);
9949                     }
9950
9951
9952                     if(this.trackResetOnLoad){
9953                         field.originalValue = field.getValue();
9954                     }
9955                 }
9956             }
9957         }
9958
9959         //Roo.each(this.childForms || [], function (f) {
9960         //    f.setValues(values);
9961         //});
9962
9963         return this;
9964     },
9965
9966     /**
9967      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9968      * they are returned as an array.
9969      * @param {Boolean} asString
9970      * @return {Object}
9971      */
9972     getValues : function(asString){
9973         //if (this.childForms) {
9974             // copy values from the child forms
9975         //    Roo.each(this.childForms, function (f) {
9976         //        this.setValues(f.getValues());
9977         //    }, this);
9978         //}
9979
9980
9981
9982         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9983         if(asString === true){
9984             return fs;
9985         }
9986         return Roo.urlDecode(fs);
9987     },
9988
9989     /**
9990      * Returns the fields in this form as an object with key/value pairs.
9991      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9992      * @return {Object}
9993      */
9994     getFieldValues : function(with_hidden)
9995     {
9996         var items = this.getItems();
9997         var ret = {};
9998         items.each(function(f){
9999             
10000             if (!f.getName()) {
10001                 return;
10002             }
10003             
10004             var v = f.getValue();
10005             
10006             if (f.inputType =='radio') {
10007                 if (typeof(ret[f.getName()]) == 'undefined') {
10008                     ret[f.getName()] = ''; // empty..
10009                 }
10010
10011                 if (!f.el.dom.checked) {
10012                     return;
10013
10014                 }
10015                 v = f.el.dom.value;
10016
10017             }
10018             
10019             if(f.xtype == 'MoneyField'){
10020                 ret[f.currencyName] = f.getCurrency();
10021             }
10022
10023             // not sure if this supported any more..
10024             if ((typeof(v) == 'object') && f.getRawValue) {
10025                 v = f.getRawValue() ; // dates..
10026             }
10027             // combo boxes where name != hiddenName...
10028             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10029                 ret[f.name] = f.getRawValue();
10030             }
10031             ret[f.getName()] = v;
10032         });
10033
10034         return ret;
10035     },
10036
10037     /**
10038      * Clears all invalid messages in this form.
10039      * @return {BasicForm} this
10040      */
10041     clearInvalid : function(){
10042         var items = this.getItems();
10043
10044         items.each(function(f){
10045            f.clearInvalid();
10046         });
10047
10048         return this;
10049     },
10050
10051     /**
10052      * Resets this form.
10053      * @return {BasicForm} this
10054      */
10055     reset : function(){
10056         var items = this.getItems();
10057         items.each(function(f){
10058             f.reset();
10059         });
10060
10061         Roo.each(this.childForms || [], function (f) {
10062             f.reset();
10063         });
10064
10065
10066         return this;
10067     },
10068     
10069     getItems : function()
10070     {
10071         var r=new Roo.util.MixedCollection(false, function(o){
10072             return o.id || (o.id = Roo.id());
10073         });
10074         var iter = function(el) {
10075             if (el.inputEl) {
10076                 r.add(el);
10077             }
10078             if (!el.items) {
10079                 return;
10080             }
10081             Roo.each(el.items,function(e) {
10082                 iter(e);
10083             });
10084         };
10085
10086         iter(this);
10087         return r;
10088     },
10089     
10090     hideFields : function(items)
10091     {
10092         Roo.each(items, function(i){
10093             
10094             var f = this.findField(i);
10095             
10096             if(!f){
10097                 return;
10098             }
10099             
10100             f.hide();
10101             
10102         }, this);
10103     },
10104     
10105     showFields : function(items)
10106     {
10107         Roo.each(items, function(i){
10108             
10109             var f = this.findField(i);
10110             
10111             if(!f){
10112                 return;
10113             }
10114             
10115             f.show();
10116             
10117         }, this);
10118     }
10119
10120 });
10121
10122 Roo.apply(Roo.bootstrap.Form, {
10123     
10124     popover : {
10125         
10126         padding : 5,
10127         
10128         isApplied : false,
10129         
10130         isMasked : false,
10131         
10132         form : false,
10133         
10134         target : false,
10135         
10136         toolTip : false,
10137         
10138         intervalID : false,
10139         
10140         maskEl : false,
10141         
10142         apply : function()
10143         {
10144             if(this.isApplied){
10145                 return;
10146             }
10147             
10148             this.maskEl = {
10149                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10150                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10151                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10152                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10153             };
10154             
10155             this.maskEl.top.enableDisplayMode("block");
10156             this.maskEl.left.enableDisplayMode("block");
10157             this.maskEl.bottom.enableDisplayMode("block");
10158             this.maskEl.right.enableDisplayMode("block");
10159             
10160             this.toolTip = new Roo.bootstrap.Tooltip({
10161                 cls : 'roo-form-error-popover',
10162                 alignment : {
10163                     'left' : ['r-l', [-2,0], 'right'],
10164                     'right' : ['l-r', [2,0], 'left'],
10165                     'bottom' : ['tl-bl', [0,2], 'top'],
10166                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10167                 }
10168             });
10169             
10170             this.toolTip.render(Roo.get(document.body));
10171
10172             this.toolTip.el.enableDisplayMode("block");
10173             
10174             Roo.get(document.body).on('click', function(){
10175                 this.unmask();
10176             }, this);
10177             
10178             Roo.get(document.body).on('touchstart', function(){
10179                 this.unmask();
10180             }, this);
10181             
10182             this.isApplied = true
10183         },
10184         
10185         mask : function(form, target)
10186         {
10187             this.form = form;
10188             
10189             this.target = target;
10190             
10191             if(!this.form.errorMask || !target.el){
10192                 return;
10193             }
10194             
10195             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10196             
10197             Roo.log(scrollable);
10198             
10199             var ot = this.target.el.calcOffsetsTo(scrollable);
10200             
10201             var scrollTo = ot[1] - this.form.maskOffset;
10202             
10203             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10204             
10205             scrollable.scrollTo('top', scrollTo);
10206             
10207             var box = this.target.el.getBox();
10208             Roo.log(box);
10209             var zIndex = Roo.bootstrap.Modal.zIndex++;
10210
10211             
10212             this.maskEl.top.setStyle('position', 'absolute');
10213             this.maskEl.top.setStyle('z-index', zIndex);
10214             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10215             this.maskEl.top.setLeft(0);
10216             this.maskEl.top.setTop(0);
10217             this.maskEl.top.show();
10218             
10219             this.maskEl.left.setStyle('position', 'absolute');
10220             this.maskEl.left.setStyle('z-index', zIndex);
10221             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10222             this.maskEl.left.setLeft(0);
10223             this.maskEl.left.setTop(box.y - this.padding);
10224             this.maskEl.left.show();
10225
10226             this.maskEl.bottom.setStyle('position', 'absolute');
10227             this.maskEl.bottom.setStyle('z-index', zIndex);
10228             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10229             this.maskEl.bottom.setLeft(0);
10230             this.maskEl.bottom.setTop(box.bottom + this.padding);
10231             this.maskEl.bottom.show();
10232
10233             this.maskEl.right.setStyle('position', 'absolute');
10234             this.maskEl.right.setStyle('z-index', zIndex);
10235             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10236             this.maskEl.right.setLeft(box.right + this.padding);
10237             this.maskEl.right.setTop(box.y - this.padding);
10238             this.maskEl.right.show();
10239
10240             this.toolTip.bindEl = this.target.el;
10241
10242             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10243
10244             var tip = this.target.blankText;
10245
10246             if(this.target.getValue() !== '' ) {
10247                 
10248                 if (this.target.invalidText.length) {
10249                     tip = this.target.invalidText;
10250                 } else if (this.target.regexText.length){
10251                     tip = this.target.regexText;
10252                 }
10253             }
10254
10255             this.toolTip.show(tip);
10256
10257             this.intervalID = window.setInterval(function() {
10258                 Roo.bootstrap.Form.popover.unmask();
10259             }, 10000);
10260
10261             window.onwheel = function(){ return false;};
10262             
10263             (function(){ this.isMasked = true; }).defer(500, this);
10264             
10265         },
10266         
10267         unmask : function()
10268         {
10269             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10270                 return;
10271             }
10272             
10273             this.maskEl.top.setStyle('position', 'absolute');
10274             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10275             this.maskEl.top.hide();
10276
10277             this.maskEl.left.setStyle('position', 'absolute');
10278             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10279             this.maskEl.left.hide();
10280
10281             this.maskEl.bottom.setStyle('position', 'absolute');
10282             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10283             this.maskEl.bottom.hide();
10284
10285             this.maskEl.right.setStyle('position', 'absolute');
10286             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10287             this.maskEl.right.hide();
10288             
10289             this.toolTip.hide();
10290             
10291             this.toolTip.el.hide();
10292             
10293             window.onwheel = function(){ return true;};
10294             
10295             if(this.intervalID){
10296                 window.clearInterval(this.intervalID);
10297                 this.intervalID = false;
10298             }
10299             
10300             this.isMasked = false;
10301             
10302         }
10303         
10304     }
10305     
10306 });
10307
10308 /*
10309  * Based on:
10310  * Ext JS Library 1.1.1
10311  * Copyright(c) 2006-2007, Ext JS, LLC.
10312  *
10313  * Originally Released Under LGPL - original licence link has changed is not relivant.
10314  *
10315  * Fork - LGPL
10316  * <script type="text/javascript">
10317  */
10318 /**
10319  * @class Roo.form.VTypes
10320  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10321  * @singleton
10322  */
10323 Roo.form.VTypes = function(){
10324     // closure these in so they are only created once.
10325     var alpha = /^[a-zA-Z_]+$/;
10326     var alphanum = /^[a-zA-Z0-9_]+$/;
10327     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10328     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10329
10330     // All these messages and functions are configurable
10331     return {
10332         /**
10333          * The function used to validate email addresses
10334          * @param {String} value The email address
10335          */
10336         'email' : function(v){
10337             return email.test(v);
10338         },
10339         /**
10340          * The error text to display when the email validation function returns false
10341          * @type String
10342          */
10343         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10344         /**
10345          * The keystroke filter mask to be applied on email input
10346          * @type RegExp
10347          */
10348         'emailMask' : /[a-z0-9_\.\-@]/i,
10349
10350         /**
10351          * The function used to validate URLs
10352          * @param {String} value The URL
10353          */
10354         'url' : function(v){
10355             return url.test(v);
10356         },
10357         /**
10358          * The error text to display when the url validation function returns false
10359          * @type String
10360          */
10361         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10362         
10363         /**
10364          * The function used to validate alpha values
10365          * @param {String} value The value
10366          */
10367         'alpha' : function(v){
10368             return alpha.test(v);
10369         },
10370         /**
10371          * The error text to display when the alpha validation function returns false
10372          * @type String
10373          */
10374         'alphaText' : 'This field should only contain letters and _',
10375         /**
10376          * The keystroke filter mask to be applied on alpha input
10377          * @type RegExp
10378          */
10379         'alphaMask' : /[a-z_]/i,
10380
10381         /**
10382          * The function used to validate alphanumeric values
10383          * @param {String} value The value
10384          */
10385         'alphanum' : function(v){
10386             return alphanum.test(v);
10387         },
10388         /**
10389          * The error text to display when the alphanumeric validation function returns false
10390          * @type String
10391          */
10392         'alphanumText' : 'This field should only contain letters, numbers and _',
10393         /**
10394          * The keystroke filter mask to be applied on alphanumeric input
10395          * @type RegExp
10396          */
10397         'alphanumMask' : /[a-z0-9_]/i
10398     };
10399 }();/*
10400  * - LGPL
10401  *
10402  * Input
10403  * 
10404  */
10405
10406 /**
10407  * @class Roo.bootstrap.Input
10408  * @extends Roo.bootstrap.Component
10409  * Bootstrap Input class
10410  * @cfg {Boolean} disabled is it disabled
10411  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10412  * @cfg {String} name name of the input
10413  * @cfg {string} fieldLabel - the label associated
10414  * @cfg {string} placeholder - placeholder to put in text.
10415  * @cfg {string}  before - input group add on before
10416  * @cfg {string} after - input group add on after
10417  * @cfg {string} size - (lg|sm) or leave empty..
10418  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10419  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10420  * @cfg {Number} md colspan out of 12 for computer-sized screens
10421  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10422  * @cfg {string} value default value of the input
10423  * @cfg {Number} labelWidth set the width of label 
10424  * @cfg {Number} labellg set the width of label (1-12)
10425  * @cfg {Number} labelmd set the width of label (1-12)
10426  * @cfg {Number} labelsm set the width of label (1-12)
10427  * @cfg {Number} labelxs set the width of label (1-12)
10428  * @cfg {String} labelAlign (top|left)
10429  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10430  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10431  * @cfg {String} indicatorpos (left|right) default left
10432  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10433  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10434  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10435
10436  * @cfg {String} align (left|center|right) Default left
10437  * @cfg {Boolean} forceFeedback (true|false) Default false
10438  * 
10439  * @constructor
10440  * Create a new Input
10441  * @param {Object} config The config object
10442  */
10443
10444 Roo.bootstrap.Input = function(config){
10445     
10446     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10447     
10448     this.addEvents({
10449         /**
10450          * @event focus
10451          * Fires when this field receives input focus.
10452          * @param {Roo.form.Field} this
10453          */
10454         focus : true,
10455         /**
10456          * @event blur
10457          * Fires when this field loses input focus.
10458          * @param {Roo.form.Field} this
10459          */
10460         blur : true,
10461         /**
10462          * @event specialkey
10463          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10464          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10465          * @param {Roo.form.Field} this
10466          * @param {Roo.EventObject} e The event object
10467          */
10468         specialkey : true,
10469         /**
10470          * @event change
10471          * Fires just before the field blurs if the field value has changed.
10472          * @param {Roo.form.Field} this
10473          * @param {Mixed} newValue The new value
10474          * @param {Mixed} oldValue The original value
10475          */
10476         change : true,
10477         /**
10478          * @event invalid
10479          * Fires after the field has been marked as invalid.
10480          * @param {Roo.form.Field} this
10481          * @param {String} msg The validation message
10482          */
10483         invalid : true,
10484         /**
10485          * @event valid
10486          * Fires after the field has been validated with no errors.
10487          * @param {Roo.form.Field} this
10488          */
10489         valid : true,
10490          /**
10491          * @event keyup
10492          * Fires after the key up
10493          * @param {Roo.form.Field} this
10494          * @param {Roo.EventObject}  e The event Object
10495          */
10496         keyup : true
10497     });
10498 };
10499
10500 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10501      /**
10502      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10503       automatic validation (defaults to "keyup").
10504      */
10505     validationEvent : "keyup",
10506      /**
10507      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10508      */
10509     validateOnBlur : true,
10510     /**
10511      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10512      */
10513     validationDelay : 250,
10514      /**
10515      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10516      */
10517     focusClass : "x-form-focus",  // not needed???
10518     
10519        
10520     /**
10521      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10522      */
10523     invalidClass : "has-warning",
10524     
10525     /**
10526      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10527      */
10528     validClass : "has-success",
10529     
10530     /**
10531      * @cfg {Boolean} hasFeedback (true|false) default true
10532      */
10533     hasFeedback : true,
10534     
10535     /**
10536      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10537      */
10538     invalidFeedbackClass : "glyphicon-warning-sign",
10539     
10540     /**
10541      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10542      */
10543     validFeedbackClass : "glyphicon-ok",
10544     
10545     /**
10546      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10547      */
10548     selectOnFocus : false,
10549     
10550      /**
10551      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10552      */
10553     maskRe : null,
10554        /**
10555      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10556      */
10557     vtype : null,
10558     
10559       /**
10560      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10561      */
10562     disableKeyFilter : false,
10563     
10564        /**
10565      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10566      */
10567     disabled : false,
10568      /**
10569      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10570      */
10571     allowBlank : true,
10572     /**
10573      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10574      */
10575     blankText : "Please complete this mandatory field",
10576     
10577      /**
10578      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10579      */
10580     minLength : 0,
10581     /**
10582      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10583      */
10584     maxLength : Number.MAX_VALUE,
10585     /**
10586      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10587      */
10588     minLengthText : "The minimum length for this field is {0}",
10589     /**
10590      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10591      */
10592     maxLengthText : "The maximum length for this field is {0}",
10593   
10594     
10595     /**
10596      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10597      * If available, this function will be called only after the basic validators all return true, and will be passed the
10598      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10599      */
10600     validator : null,
10601     /**
10602      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10603      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10604      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10605      */
10606     regex : null,
10607     /**
10608      * @cfg {String} regexText -- Depricated - use Invalid Text
10609      */
10610     regexText : "",
10611     
10612     /**
10613      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10614      */
10615     invalidText : "",
10616     
10617     
10618     
10619     autocomplete: false,
10620     
10621     
10622     fieldLabel : '',
10623     inputType : 'text',
10624     
10625     name : false,
10626     placeholder: false,
10627     before : false,
10628     after : false,
10629     size : false,
10630     hasFocus : false,
10631     preventMark: false,
10632     isFormField : true,
10633     value : '',
10634     labelWidth : 2,
10635     labelAlign : false,
10636     readOnly : false,
10637     align : false,
10638     formatedValue : false,
10639     forceFeedback : false,
10640     
10641     indicatorpos : 'left',
10642     
10643     labellg : 0,
10644     labelmd : 0,
10645     labelsm : 0,
10646     labelxs : 0,
10647     
10648     capture : '',
10649     accept : '',
10650     
10651     parentLabelAlign : function()
10652     {
10653         var parent = this;
10654         while (parent.parent()) {
10655             parent = parent.parent();
10656             if (typeof(parent.labelAlign) !='undefined') {
10657                 return parent.labelAlign;
10658             }
10659         }
10660         return 'left';
10661         
10662     },
10663     
10664     getAutoCreate : function()
10665     {
10666         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10667         
10668         var id = Roo.id();
10669         
10670         var cfg = {};
10671         
10672         if(this.inputType != 'hidden'){
10673             cfg.cls = 'form-group' //input-group
10674         }
10675         
10676         var input =  {
10677             tag: 'input',
10678             id : id,
10679             type : this.inputType,
10680             value : this.value,
10681             cls : 'form-control',
10682             placeholder : this.placeholder || '',
10683             autocomplete : this.autocomplete || 'new-password'
10684         };
10685         if (this.inputType == 'file') {
10686             input.style = 'overflow:hidden'; // why not in CSS?
10687         }
10688         
10689         if(this.capture.length){
10690             input.capture = this.capture;
10691         }
10692         
10693         if(this.accept.length){
10694             input.accept = this.accept + "/*";
10695         }
10696         
10697         if(this.align){
10698             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10699         }
10700         
10701         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10702             input.maxLength = this.maxLength;
10703         }
10704         
10705         if (this.disabled) {
10706             input.disabled=true;
10707         }
10708         
10709         if (this.readOnly) {
10710             input.readonly=true;
10711         }
10712         
10713         if (this.name) {
10714             input.name = this.name;
10715         }
10716         
10717         if (this.size) {
10718             input.cls += ' input-' + this.size;
10719         }
10720         
10721         var settings=this;
10722         ['xs','sm','md','lg'].map(function(size){
10723             if (settings[size]) {
10724                 cfg.cls += ' col-' + size + '-' + settings[size];
10725             }
10726         });
10727         
10728         var inputblock = input;
10729         
10730         var feedback = {
10731             tag: 'span',
10732             cls: 'glyphicon form-control-feedback'
10733         };
10734             
10735         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10736             
10737             inputblock = {
10738                 cls : 'has-feedback',
10739                 cn :  [
10740                     input,
10741                     feedback
10742                 ] 
10743             };  
10744         }
10745         
10746         if (this.before || this.after) {
10747             
10748             inputblock = {
10749                 cls : 'input-group',
10750                 cn :  [] 
10751             };
10752             
10753             if (this.before && typeof(this.before) == 'string') {
10754                 
10755                 inputblock.cn.push({
10756                     tag :'span',
10757                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10758                     html : this.before
10759                 });
10760             }
10761             if (this.before && typeof(this.before) == 'object') {
10762                 this.before = Roo.factory(this.before);
10763                 
10764                 inputblock.cn.push({
10765                     tag :'span',
10766                     cls : 'roo-input-before input-group-prepend   input-group-' +
10767                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10768                 });
10769             }
10770             
10771             inputblock.cn.push(input);
10772             
10773             if (this.after && typeof(this.after) == 'string') {
10774                 inputblock.cn.push({
10775                     tag :'span',
10776                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10777                     html : this.after
10778                 });
10779             }
10780             if (this.after && typeof(this.after) == 'object') {
10781                 this.after = Roo.factory(this.after);
10782                 
10783                 inputblock.cn.push({
10784                     tag :'span',
10785                     cls : 'roo-input-after input-group-append  input-group-' +
10786                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10787                 });
10788             }
10789             
10790             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10791                 inputblock.cls += ' has-feedback';
10792                 inputblock.cn.push(feedback);
10793             }
10794         };
10795         var indicator = {
10796             tag : 'i',
10797             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10798             tooltip : 'This field is required'
10799         };
10800         if (this.allowBlank ) {
10801             indicator.style = this.allowBlank ? ' display:none' : '';
10802         }
10803         if (align ==='left' && this.fieldLabel.length) {
10804             
10805             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10806             
10807             cfg.cn = [
10808                 indicator,
10809                 {
10810                     tag: 'label',
10811                     'for' :  id,
10812                     cls : 'control-label col-form-label',
10813                     html : this.fieldLabel
10814
10815                 },
10816                 {
10817                     cls : "", 
10818                     cn: [
10819                         inputblock
10820                     ]
10821                 }
10822             ];
10823             
10824             var labelCfg = cfg.cn[1];
10825             var contentCfg = cfg.cn[2];
10826             
10827             if(this.indicatorpos == 'right'){
10828                 cfg.cn = [
10829                     {
10830                         tag: 'label',
10831                         'for' :  id,
10832                         cls : 'control-label col-form-label',
10833                         cn : [
10834                             {
10835                                 tag : 'span',
10836                                 html : this.fieldLabel
10837                             },
10838                             indicator
10839                         ]
10840                     },
10841                     {
10842                         cls : "",
10843                         cn: [
10844                             inputblock
10845                         ]
10846                     }
10847
10848                 ];
10849                 
10850                 labelCfg = cfg.cn[0];
10851                 contentCfg = cfg.cn[1];
10852             
10853             }
10854             
10855             if(this.labelWidth > 12){
10856                 labelCfg.style = "width: " + this.labelWidth + 'px';
10857             }
10858             
10859             if(this.labelWidth < 13 && this.labelmd == 0){
10860                 this.labelmd = this.labelWidth;
10861             }
10862             
10863             if(this.labellg > 0){
10864                 labelCfg.cls += ' col-lg-' + this.labellg;
10865                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10866             }
10867             
10868             if(this.labelmd > 0){
10869                 labelCfg.cls += ' col-md-' + this.labelmd;
10870                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10871             }
10872             
10873             if(this.labelsm > 0){
10874                 labelCfg.cls += ' col-sm-' + this.labelsm;
10875                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10876             }
10877             
10878             if(this.labelxs > 0){
10879                 labelCfg.cls += ' col-xs-' + this.labelxs;
10880                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10881             }
10882             
10883             
10884         } else if ( this.fieldLabel.length) {
10885                 
10886             
10887             
10888             cfg.cn = [
10889                 {
10890                     tag : 'i',
10891                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10892                     tooltip : 'This field is required',
10893                     style : this.allowBlank ? ' display:none' : '' 
10894                 },
10895                 {
10896                     tag: 'label',
10897                    //cls : 'input-group-addon',
10898                     html : this.fieldLabel
10899
10900                 },
10901
10902                inputblock
10903
10904            ];
10905            
10906            if(this.indicatorpos == 'right'){
10907        
10908                 cfg.cn = [
10909                     {
10910                         tag: 'label',
10911                        //cls : 'input-group-addon',
10912                         html : this.fieldLabel
10913
10914                     },
10915                     {
10916                         tag : 'i',
10917                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10918                         tooltip : 'This field is required',
10919                         style : this.allowBlank ? ' display:none' : '' 
10920                     },
10921
10922                    inputblock
10923
10924                ];
10925
10926             }
10927
10928         } else {
10929             
10930             cfg.cn = [
10931
10932                     inputblock
10933
10934             ];
10935                 
10936                 
10937         };
10938         
10939         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10940            cfg.cls += ' navbar-form';
10941         }
10942         
10943         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10944             // on BS4 we do this only if not form 
10945             cfg.cls += ' navbar-form';
10946             cfg.tag = 'li';
10947         }
10948         
10949         return cfg;
10950         
10951     },
10952     /**
10953      * return the real input element.
10954      */
10955     inputEl: function ()
10956     {
10957         return this.el.select('input.form-control',true).first();
10958     },
10959     
10960     tooltipEl : function()
10961     {
10962         return this.inputEl();
10963     },
10964     
10965     indicatorEl : function()
10966     {
10967         if (Roo.bootstrap.version == 4) {
10968             return false; // not enabled in v4 yet.
10969         }
10970         
10971         var indicator = this.el.select('i.roo-required-indicator',true).first();
10972         
10973         if(!indicator){
10974             return false;
10975         }
10976         
10977         return indicator;
10978         
10979     },
10980     
10981     setDisabled : function(v)
10982     {
10983         var i  = this.inputEl().dom;
10984         if (!v) {
10985             i.removeAttribute('disabled');
10986             return;
10987             
10988         }
10989         i.setAttribute('disabled','true');
10990     },
10991     initEvents : function()
10992     {
10993           
10994         this.inputEl().on("keydown" , this.fireKey,  this);
10995         this.inputEl().on("focus", this.onFocus,  this);
10996         this.inputEl().on("blur", this.onBlur,  this);
10997         
10998         this.inputEl().relayEvent('keyup', this);
10999         
11000         this.indicator = this.indicatorEl();
11001         
11002         if(this.indicator){
11003             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11004         }
11005  
11006         // reference to original value for reset
11007         this.originalValue = this.getValue();
11008         //Roo.form.TextField.superclass.initEvents.call(this);
11009         if(this.validationEvent == 'keyup'){
11010             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11011             this.inputEl().on('keyup', this.filterValidation, this);
11012         }
11013         else if(this.validationEvent !== false){
11014             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11015         }
11016         
11017         if(this.selectOnFocus){
11018             this.on("focus", this.preFocus, this);
11019             
11020         }
11021         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11022             this.inputEl().on("keypress", this.filterKeys, this);
11023         } else {
11024             this.inputEl().relayEvent('keypress', this);
11025         }
11026        /* if(this.grow){
11027             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11028             this.el.on("click", this.autoSize,  this);
11029         }
11030         */
11031         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11032             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11033         }
11034         
11035         if (typeof(this.before) == 'object') {
11036             this.before.render(this.el.select('.roo-input-before',true).first());
11037         }
11038         if (typeof(this.after) == 'object') {
11039             this.after.render(this.el.select('.roo-input-after',true).first());
11040         }
11041         
11042         this.inputEl().on('change', this.onChange, this);
11043         
11044     },
11045     filterValidation : function(e){
11046         if(!e.isNavKeyPress()){
11047             this.validationTask.delay(this.validationDelay);
11048         }
11049     },
11050      /**
11051      * Validates the field value
11052      * @return {Boolean} True if the value is valid, else false
11053      */
11054     validate : function(){
11055         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11056         if(this.disabled || this.validateValue(this.getRawValue())){
11057             this.markValid();
11058             return true;
11059         }
11060         
11061         this.markInvalid();
11062         return false;
11063     },
11064     
11065     
11066     /**
11067      * Validates a value according to the field's validation rules and marks the field as invalid
11068      * if the validation fails
11069      * @param {Mixed} value The value to validate
11070      * @return {Boolean} True if the value is valid, else false
11071      */
11072     validateValue : function(value)
11073     {
11074         if(this.getVisibilityEl().hasClass('hidden')){
11075             return true;
11076         }
11077         
11078         if(value.length < 1)  { // if it's blank
11079             if(this.allowBlank){
11080                 return true;
11081             }
11082             return false;
11083         }
11084         
11085         if(value.length < this.minLength){
11086             return false;
11087         }
11088         if(value.length > this.maxLength){
11089             return false;
11090         }
11091         if(this.vtype){
11092             var vt = Roo.form.VTypes;
11093             if(!vt[this.vtype](value, this)){
11094                 return false;
11095             }
11096         }
11097         if(typeof this.validator == "function"){
11098             var msg = this.validator(value);
11099             if(msg !== true){
11100                 return false;
11101             }
11102             if (typeof(msg) == 'string') {
11103                 this.invalidText = msg;
11104             }
11105         }
11106         
11107         if(this.regex && !this.regex.test(value)){
11108             return false;
11109         }
11110         
11111         return true;
11112     },
11113     
11114      // private
11115     fireKey : function(e){
11116         //Roo.log('field ' + e.getKey());
11117         if(e.isNavKeyPress()){
11118             this.fireEvent("specialkey", this, e);
11119         }
11120     },
11121     focus : function (selectText){
11122         if(this.rendered){
11123             this.inputEl().focus();
11124             if(selectText === true){
11125                 this.inputEl().dom.select();
11126             }
11127         }
11128         return this;
11129     } ,
11130     
11131     onFocus : function(){
11132         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11133            // this.el.addClass(this.focusClass);
11134         }
11135         if(!this.hasFocus){
11136             this.hasFocus = true;
11137             this.startValue = this.getValue();
11138             this.fireEvent("focus", this);
11139         }
11140     },
11141     
11142     beforeBlur : Roo.emptyFn,
11143
11144     
11145     // private
11146     onBlur : function(){
11147         this.beforeBlur();
11148         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11149             //this.el.removeClass(this.focusClass);
11150         }
11151         this.hasFocus = false;
11152         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11153             this.validate();
11154         }
11155         var v = this.getValue();
11156         if(String(v) !== String(this.startValue)){
11157             this.fireEvent('change', this, v, this.startValue);
11158         }
11159         this.fireEvent("blur", this);
11160     },
11161     
11162     onChange : function(e)
11163     {
11164         var v = this.getValue();
11165         if(String(v) !== String(this.startValue)){
11166             this.fireEvent('change', this, v, this.startValue);
11167         }
11168         
11169     },
11170     
11171     /**
11172      * Resets the current field value to the originally loaded value and clears any validation messages
11173      */
11174     reset : function(){
11175         this.setValue(this.originalValue);
11176         this.validate();
11177     },
11178      /**
11179      * Returns the name of the field
11180      * @return {Mixed} name The name field
11181      */
11182     getName: function(){
11183         return this.name;
11184     },
11185      /**
11186      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11187      * @return {Mixed} value The field value
11188      */
11189     getValue : function(){
11190         
11191         var v = this.inputEl().getValue();
11192         
11193         return v;
11194     },
11195     /**
11196      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11197      * @return {Mixed} value The field value
11198      */
11199     getRawValue : function(){
11200         var v = this.inputEl().getValue();
11201         
11202         return v;
11203     },
11204     
11205     /**
11206      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11207      * @param {Mixed} value The value to set
11208      */
11209     setRawValue : function(v){
11210         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11211     },
11212     
11213     selectText : function(start, end){
11214         var v = this.getRawValue();
11215         if(v.length > 0){
11216             start = start === undefined ? 0 : start;
11217             end = end === undefined ? v.length : end;
11218             var d = this.inputEl().dom;
11219             if(d.setSelectionRange){
11220                 d.setSelectionRange(start, end);
11221             }else if(d.createTextRange){
11222                 var range = d.createTextRange();
11223                 range.moveStart("character", start);
11224                 range.moveEnd("character", v.length-end);
11225                 range.select();
11226             }
11227         }
11228     },
11229     
11230     /**
11231      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11232      * @param {Mixed} value The value to set
11233      */
11234     setValue : function(v){
11235         this.value = v;
11236         if(this.rendered){
11237             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11238             this.validate();
11239         }
11240     },
11241     
11242     /*
11243     processValue : function(value){
11244         if(this.stripCharsRe){
11245             var newValue = value.replace(this.stripCharsRe, '');
11246             if(newValue !== value){
11247                 this.setRawValue(newValue);
11248                 return newValue;
11249             }
11250         }
11251         return value;
11252     },
11253   */
11254     preFocus : function(){
11255         
11256         if(this.selectOnFocus){
11257             this.inputEl().dom.select();
11258         }
11259     },
11260     filterKeys : function(e){
11261         var k = e.getKey();
11262         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11263             return;
11264         }
11265         var c = e.getCharCode(), cc = String.fromCharCode(c);
11266         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11267             return;
11268         }
11269         if(!this.maskRe.test(cc)){
11270             e.stopEvent();
11271         }
11272     },
11273      /**
11274      * Clear any invalid styles/messages for this field
11275      */
11276     clearInvalid : function(){
11277         
11278         if(!this.el || this.preventMark){ // not rendered
11279             return;
11280         }
11281         
11282         
11283         this.el.removeClass([this.invalidClass, 'is-invalid']);
11284         
11285         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11286             
11287             var feedback = this.el.select('.form-control-feedback', true).first();
11288             
11289             if(feedback){
11290                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11291             }
11292             
11293         }
11294         
11295         if(this.indicator){
11296             this.indicator.removeClass('visible');
11297             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11298         }
11299         
11300         this.fireEvent('valid', this);
11301     },
11302     
11303      /**
11304      * Mark this field as valid
11305      */
11306     markValid : function()
11307     {
11308         if(!this.el  || this.preventMark){ // not rendered...
11309             return;
11310         }
11311         
11312         this.el.removeClass([this.invalidClass, this.validClass]);
11313         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11314
11315         var feedback = this.el.select('.form-control-feedback', true).first();
11316             
11317         if(feedback){
11318             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11319         }
11320         
11321         if(this.indicator){
11322             this.indicator.removeClass('visible');
11323             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11324         }
11325         
11326         if(this.disabled){
11327             return;
11328         }
11329         
11330            
11331         if(this.allowBlank && !this.getRawValue().length){
11332             return;
11333         }
11334         if (Roo.bootstrap.version == 3) {
11335             this.el.addClass(this.validClass);
11336         } else {
11337             this.inputEl().addClass('is-valid');
11338         }
11339
11340         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11341             
11342             var feedback = this.el.select('.form-control-feedback', true).first();
11343             
11344             if(feedback){
11345                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11346                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11347             }
11348             
11349         }
11350         
11351         this.fireEvent('valid', this);
11352     },
11353     
11354      /**
11355      * Mark this field as invalid
11356      * @param {String} msg The validation message
11357      */
11358     markInvalid : function(msg)
11359     {
11360         if(!this.el  || this.preventMark){ // not rendered
11361             return;
11362         }
11363         
11364         this.el.removeClass([this.invalidClass, this.validClass]);
11365         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11366         
11367         var feedback = this.el.select('.form-control-feedback', true).first();
11368             
11369         if(feedback){
11370             this.el.select('.form-control-feedback', true).first().removeClass(
11371                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11372         }
11373
11374         if(this.disabled){
11375             return;
11376         }
11377         
11378         if(this.allowBlank && !this.getRawValue().length){
11379             return;
11380         }
11381         
11382         if(this.indicator){
11383             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11384             this.indicator.addClass('visible');
11385         }
11386         if (Roo.bootstrap.version == 3) {
11387             this.el.addClass(this.invalidClass);
11388         } else {
11389             this.inputEl().addClass('is-invalid');
11390         }
11391         
11392         
11393         
11394         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11395             
11396             var feedback = this.el.select('.form-control-feedback', true).first();
11397             
11398             if(feedback){
11399                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11400                 
11401                 if(this.getValue().length || this.forceFeedback){
11402                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11403                 }
11404                 
11405             }
11406             
11407         }
11408         
11409         this.fireEvent('invalid', this, msg);
11410     },
11411     // private
11412     SafariOnKeyDown : function(event)
11413     {
11414         // this is a workaround for a password hang bug on chrome/ webkit.
11415         if (this.inputEl().dom.type != 'password') {
11416             return;
11417         }
11418         
11419         var isSelectAll = false;
11420         
11421         if(this.inputEl().dom.selectionEnd > 0){
11422             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11423         }
11424         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11425             event.preventDefault();
11426             this.setValue('');
11427             return;
11428         }
11429         
11430         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11431             
11432             event.preventDefault();
11433             // this is very hacky as keydown always get's upper case.
11434             //
11435             var cc = String.fromCharCode(event.getCharCode());
11436             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11437             
11438         }
11439     },
11440     adjustWidth : function(tag, w){
11441         tag = tag.toLowerCase();
11442         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11443             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11444                 if(tag == 'input'){
11445                     return w + 2;
11446                 }
11447                 if(tag == 'textarea'){
11448                     return w-2;
11449                 }
11450             }else if(Roo.isOpera){
11451                 if(tag == 'input'){
11452                     return w + 2;
11453                 }
11454                 if(tag == 'textarea'){
11455                     return w-2;
11456                 }
11457             }
11458         }
11459         return w;
11460     },
11461     
11462     setFieldLabel : function(v)
11463     {
11464         if(!this.rendered){
11465             return;
11466         }
11467         
11468         if(this.indicatorEl()){
11469             var ar = this.el.select('label > span',true);
11470             
11471             if (ar.elements.length) {
11472                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11473                 this.fieldLabel = v;
11474                 return;
11475             }
11476             
11477             var br = this.el.select('label',true);
11478             
11479             if(br.elements.length) {
11480                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11481                 this.fieldLabel = v;
11482                 return;
11483             }
11484             
11485             Roo.log('Cannot Found any of label > span || label in input');
11486             return;
11487         }
11488         
11489         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11490         this.fieldLabel = v;
11491         
11492         
11493     }
11494 });
11495
11496  
11497 /*
11498  * - LGPL
11499  *
11500  * Input
11501  * 
11502  */
11503
11504 /**
11505  * @class Roo.bootstrap.TextArea
11506  * @extends Roo.bootstrap.Input
11507  * Bootstrap TextArea class
11508  * @cfg {Number} cols Specifies the visible width of a text area
11509  * @cfg {Number} rows Specifies the visible number of lines in a text area
11510  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11511  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11512  * @cfg {string} html text
11513  * 
11514  * @constructor
11515  * Create a new TextArea
11516  * @param {Object} config The config object
11517  */
11518
11519 Roo.bootstrap.TextArea = function(config){
11520     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11521    
11522 };
11523
11524 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11525      
11526     cols : false,
11527     rows : 5,
11528     readOnly : false,
11529     warp : 'soft',
11530     resize : false,
11531     value: false,
11532     html: false,
11533     
11534     getAutoCreate : function(){
11535         
11536         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11537         
11538         var id = Roo.id();
11539         
11540         var cfg = {};
11541         
11542         if(this.inputType != 'hidden'){
11543             cfg.cls = 'form-group' //input-group
11544         }
11545         
11546         var input =  {
11547             tag: 'textarea',
11548             id : id,
11549             warp : this.warp,
11550             rows : this.rows,
11551             value : this.value || '',
11552             html: this.html || '',
11553             cls : 'form-control',
11554             placeholder : this.placeholder || '' 
11555             
11556         };
11557         
11558         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11559             input.maxLength = this.maxLength;
11560         }
11561         
11562         if(this.resize){
11563             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11564         }
11565         
11566         if(this.cols){
11567             input.cols = this.cols;
11568         }
11569         
11570         if (this.readOnly) {
11571             input.readonly = true;
11572         }
11573         
11574         if (this.name) {
11575             input.name = this.name;
11576         }
11577         
11578         if (this.size) {
11579             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11580         }
11581         
11582         var settings=this;
11583         ['xs','sm','md','lg'].map(function(size){
11584             if (settings[size]) {
11585                 cfg.cls += ' col-' + size + '-' + settings[size];
11586             }
11587         });
11588         
11589         var inputblock = input;
11590         
11591         if(this.hasFeedback && !this.allowBlank){
11592             
11593             var feedback = {
11594                 tag: 'span',
11595                 cls: 'glyphicon form-control-feedback'
11596             };
11597
11598             inputblock = {
11599                 cls : 'has-feedback',
11600                 cn :  [
11601                     input,
11602                     feedback
11603                 ] 
11604             };  
11605         }
11606         
11607         
11608         if (this.before || this.after) {
11609             
11610             inputblock = {
11611                 cls : 'input-group',
11612                 cn :  [] 
11613             };
11614             if (this.before) {
11615                 inputblock.cn.push({
11616                     tag :'span',
11617                     cls : 'input-group-addon',
11618                     html : this.before
11619                 });
11620             }
11621             
11622             inputblock.cn.push(input);
11623             
11624             if(this.hasFeedback && !this.allowBlank){
11625                 inputblock.cls += ' has-feedback';
11626                 inputblock.cn.push(feedback);
11627             }
11628             
11629             if (this.after) {
11630                 inputblock.cn.push({
11631                     tag :'span',
11632                     cls : 'input-group-addon',
11633                     html : this.after
11634                 });
11635             }
11636             
11637         }
11638         
11639         if (align ==='left' && this.fieldLabel.length) {
11640             cfg.cn = [
11641                 {
11642                     tag: 'label',
11643                     'for' :  id,
11644                     cls : 'control-label',
11645                     html : this.fieldLabel
11646                 },
11647                 {
11648                     cls : "",
11649                     cn: [
11650                         inputblock
11651                     ]
11652                 }
11653
11654             ];
11655             
11656             if(this.labelWidth > 12){
11657                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11658             }
11659
11660             if(this.labelWidth < 13 && this.labelmd == 0){
11661                 this.labelmd = this.labelWidth;
11662             }
11663
11664             if(this.labellg > 0){
11665                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11666                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11667             }
11668
11669             if(this.labelmd > 0){
11670                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11671                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11672             }
11673
11674             if(this.labelsm > 0){
11675                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11676                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11677             }
11678
11679             if(this.labelxs > 0){
11680                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11681                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11682             }
11683             
11684         } else if ( this.fieldLabel.length) {
11685             cfg.cn = [
11686
11687                {
11688                    tag: 'label',
11689                    //cls : 'input-group-addon',
11690                    html : this.fieldLabel
11691
11692                },
11693
11694                inputblock
11695
11696            ];
11697
11698         } else {
11699
11700             cfg.cn = [
11701
11702                 inputblock
11703
11704             ];
11705                 
11706         }
11707         
11708         if (this.disabled) {
11709             input.disabled=true;
11710         }
11711         
11712         return cfg;
11713         
11714     },
11715     /**
11716      * return the real textarea element.
11717      */
11718     inputEl: function ()
11719     {
11720         return this.el.select('textarea.form-control',true).first();
11721     },
11722     
11723     /**
11724      * Clear any invalid styles/messages for this field
11725      */
11726     clearInvalid : function()
11727     {
11728         
11729         if(!this.el || this.preventMark){ // not rendered
11730             return;
11731         }
11732         
11733         var label = this.el.select('label', true).first();
11734         var icon = this.el.select('i.fa-star', true).first();
11735         
11736         if(label && icon){
11737             icon.remove();
11738         }
11739         this.el.removeClass( this.validClass);
11740         this.inputEl().removeClass('is-invalid');
11741          
11742         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11743             
11744             var feedback = this.el.select('.form-control-feedback', true).first();
11745             
11746             if(feedback){
11747                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11748             }
11749             
11750         }
11751         
11752         this.fireEvent('valid', this);
11753     },
11754     
11755      /**
11756      * Mark this field as valid
11757      */
11758     markValid : function()
11759     {
11760         if(!this.el  || this.preventMark){ // not rendered
11761             return;
11762         }
11763         
11764         this.el.removeClass([this.invalidClass, this.validClass]);
11765         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11766         
11767         var feedback = this.el.select('.form-control-feedback', true).first();
11768             
11769         if(feedback){
11770             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11771         }
11772
11773         if(this.disabled || this.allowBlank){
11774             return;
11775         }
11776         
11777         var label = this.el.select('label', true).first();
11778         var icon = this.el.select('i.fa-star', true).first();
11779         
11780         if(label && icon){
11781             icon.remove();
11782         }
11783         if (Roo.bootstrap.version == 3) {
11784             this.el.addClass(this.validClass);
11785         } else {
11786             this.inputEl().addClass('is-valid');
11787         }
11788         
11789         
11790         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11791             
11792             var feedback = this.el.select('.form-control-feedback', true).first();
11793             
11794             if(feedback){
11795                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11796                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11797             }
11798             
11799         }
11800         
11801         this.fireEvent('valid', this);
11802     },
11803     
11804      /**
11805      * Mark this field as invalid
11806      * @param {String} msg The validation message
11807      */
11808     markInvalid : function(msg)
11809     {
11810         if(!this.el  || this.preventMark){ // not rendered
11811             return;
11812         }
11813         
11814         this.el.removeClass([this.invalidClass, this.validClass]);
11815         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11816         
11817         var feedback = this.el.select('.form-control-feedback', true).first();
11818             
11819         if(feedback){
11820             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11821         }
11822
11823         if(this.disabled || this.allowBlank){
11824             return;
11825         }
11826         
11827         var label = this.el.select('label', true).first();
11828         var icon = this.el.select('i.fa-star', true).first();
11829         
11830         if(!this.getValue().length && label && !icon){
11831             this.el.createChild({
11832                 tag : 'i',
11833                 cls : 'text-danger fa fa-lg fa-star',
11834                 tooltip : 'This field is required',
11835                 style : 'margin-right:5px;'
11836             }, label, true);
11837         }
11838         
11839         if (Roo.bootstrap.version == 3) {
11840             this.el.addClass(this.invalidClass);
11841         } else {
11842             this.inputEl().addClass('is-invalid');
11843         }
11844         
11845         // fixme ... this may be depricated need to test..
11846         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11847             
11848             var feedback = this.el.select('.form-control-feedback', true).first();
11849             
11850             if(feedback){
11851                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11852                 
11853                 if(this.getValue().length || this.forceFeedback){
11854                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11855                 }
11856                 
11857             }
11858             
11859         }
11860         
11861         this.fireEvent('invalid', this, msg);
11862     }
11863 });
11864
11865  
11866 /*
11867  * - LGPL
11868  *
11869  * trigger field - base class for combo..
11870  * 
11871  */
11872  
11873 /**
11874  * @class Roo.bootstrap.TriggerField
11875  * @extends Roo.bootstrap.Input
11876  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11877  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11878  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11879  * for which you can provide a custom implementation.  For example:
11880  * <pre><code>
11881 var trigger = new Roo.bootstrap.TriggerField();
11882 trigger.onTriggerClick = myTriggerFn;
11883 trigger.applyTo('my-field');
11884 </code></pre>
11885  *
11886  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11887  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11888  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11889  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11890  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11891
11892  * @constructor
11893  * Create a new TriggerField.
11894  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11895  * to the base TextField)
11896  */
11897 Roo.bootstrap.TriggerField = function(config){
11898     this.mimicing = false;
11899     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11900 };
11901
11902 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11903     /**
11904      * @cfg {String} triggerClass A CSS class to apply to the trigger
11905      */
11906      /**
11907      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11908      */
11909     hideTrigger:false,
11910
11911     /**
11912      * @cfg {Boolean} removable (true|false) special filter default false
11913      */
11914     removable : false,
11915     
11916     /** @cfg {Boolean} grow @hide */
11917     /** @cfg {Number} growMin @hide */
11918     /** @cfg {Number} growMax @hide */
11919
11920     /**
11921      * @hide 
11922      * @method
11923      */
11924     autoSize: Roo.emptyFn,
11925     // private
11926     monitorTab : true,
11927     // private
11928     deferHeight : true,
11929
11930     
11931     actionMode : 'wrap',
11932     
11933     caret : false,
11934     
11935     
11936     getAutoCreate : function(){
11937        
11938         var align = this.labelAlign || this.parentLabelAlign();
11939         
11940         var id = Roo.id();
11941         
11942         var cfg = {
11943             cls: 'form-group' //input-group
11944         };
11945         
11946         
11947         var input =  {
11948             tag: 'input',
11949             id : id,
11950             type : this.inputType,
11951             cls : 'form-control',
11952             autocomplete: 'new-password',
11953             placeholder : this.placeholder || '' 
11954             
11955         };
11956         if (this.name) {
11957             input.name = this.name;
11958         }
11959         if (this.size) {
11960             input.cls += ' input-' + this.size;
11961         }
11962         
11963         if (this.disabled) {
11964             input.disabled=true;
11965         }
11966         
11967         var inputblock = input;
11968         
11969         if(this.hasFeedback && !this.allowBlank){
11970             
11971             var feedback = {
11972                 tag: 'span',
11973                 cls: 'glyphicon form-control-feedback'
11974             };
11975             
11976             if(this.removable && !this.editable  ){
11977                 inputblock = {
11978                     cls : 'has-feedback',
11979                     cn :  [
11980                         inputblock,
11981                         {
11982                             tag: 'button',
11983                             html : 'x',
11984                             cls : 'roo-combo-removable-btn close'
11985                         },
11986                         feedback
11987                     ] 
11988                 };
11989             } else {
11990                 inputblock = {
11991                     cls : 'has-feedback',
11992                     cn :  [
11993                         inputblock,
11994                         feedback
11995                     ] 
11996                 };
11997             }
11998
11999         } else {
12000             if(this.removable && !this.editable ){
12001                 inputblock = {
12002                     cls : 'roo-removable',
12003                     cn :  [
12004                         inputblock,
12005                         {
12006                             tag: 'button',
12007                             html : 'x',
12008                             cls : 'roo-combo-removable-btn close'
12009                         }
12010                     ] 
12011                 };
12012             }
12013         }
12014         
12015         if (this.before || this.after) {
12016             
12017             inputblock = {
12018                 cls : 'input-group',
12019                 cn :  [] 
12020             };
12021             if (this.before) {
12022                 inputblock.cn.push({
12023                     tag :'span',
12024                     cls : 'input-group-addon input-group-prepend input-group-text',
12025                     html : this.before
12026                 });
12027             }
12028             
12029             inputblock.cn.push(input);
12030             
12031             if(this.hasFeedback && !this.allowBlank){
12032                 inputblock.cls += ' has-feedback';
12033                 inputblock.cn.push(feedback);
12034             }
12035             
12036             if (this.after) {
12037                 inputblock.cn.push({
12038                     tag :'span',
12039                     cls : 'input-group-addon input-group-append input-group-text',
12040                     html : this.after
12041                 });
12042             }
12043             
12044         };
12045         
12046       
12047         
12048         var ibwrap = inputblock;
12049         
12050         if(this.multiple){
12051             ibwrap = {
12052                 tag: 'ul',
12053                 cls: 'roo-select2-choices',
12054                 cn:[
12055                     {
12056                         tag: 'li',
12057                         cls: 'roo-select2-search-field',
12058                         cn: [
12059
12060                             inputblock
12061                         ]
12062                     }
12063                 ]
12064             };
12065                 
12066         }
12067         
12068         var combobox = {
12069             cls: 'roo-select2-container input-group',
12070             cn: [
12071                  {
12072                     tag: 'input',
12073                     type : 'hidden',
12074                     cls: 'form-hidden-field'
12075                 },
12076                 ibwrap
12077             ]
12078         };
12079         
12080         if(!this.multiple && this.showToggleBtn){
12081             
12082             var caret = {
12083                         tag: 'span',
12084                         cls: 'caret'
12085              };
12086             if (this.caret != false) {
12087                 caret = {
12088                      tag: 'i',
12089                      cls: 'fa fa-' + this.caret
12090                 };
12091                 
12092             }
12093             
12094             combobox.cn.push({
12095                 tag :'span',
12096                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12097                 cn : [
12098                     Roo.bootstrap.version == 3 ? caret : '',
12099                     {
12100                         tag: 'span',
12101                         cls: 'combobox-clear',
12102                         cn  : [
12103                             {
12104                                 tag : 'i',
12105                                 cls: 'icon-remove'
12106                             }
12107                         ]
12108                     }
12109                 ]
12110
12111             })
12112         }
12113         
12114         if(this.multiple){
12115             combobox.cls += ' roo-select2-container-multi';
12116         }
12117          var indicator = {
12118             tag : 'i',
12119             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12120             tooltip : 'This field is required'
12121         };
12122         if (Roo.bootstrap.version == 4) {
12123             indicator = {
12124                 tag : 'i',
12125                 style : 'display:none'
12126             };
12127         }
12128         
12129         
12130         if (align ==='left' && this.fieldLabel.length) {
12131             
12132             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12133
12134             cfg.cn = [
12135                 indicator,
12136                 {
12137                     tag: 'label',
12138                     'for' :  id,
12139                     cls : 'control-label',
12140                     html : this.fieldLabel
12141
12142                 },
12143                 {
12144                     cls : "", 
12145                     cn: [
12146                         combobox
12147                     ]
12148                 }
12149
12150             ];
12151             
12152             var labelCfg = cfg.cn[1];
12153             var contentCfg = cfg.cn[2];
12154             
12155             if(this.indicatorpos == 'right'){
12156                 cfg.cn = [
12157                     {
12158                         tag: 'label',
12159                         'for' :  id,
12160                         cls : 'control-label',
12161                         cn : [
12162                             {
12163                                 tag : 'span',
12164                                 html : this.fieldLabel
12165                             },
12166                             indicator
12167                         ]
12168                     },
12169                     {
12170                         cls : "", 
12171                         cn: [
12172                             combobox
12173                         ]
12174                     }
12175
12176                 ];
12177                 
12178                 labelCfg = cfg.cn[0];
12179                 contentCfg = cfg.cn[1];
12180             }
12181             
12182             if(this.labelWidth > 12){
12183                 labelCfg.style = "width: " + this.labelWidth + 'px';
12184             }
12185             
12186             if(this.labelWidth < 13 && this.labelmd == 0){
12187                 this.labelmd = this.labelWidth;
12188             }
12189             
12190             if(this.labellg > 0){
12191                 labelCfg.cls += ' col-lg-' + this.labellg;
12192                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12193             }
12194             
12195             if(this.labelmd > 0){
12196                 labelCfg.cls += ' col-md-' + this.labelmd;
12197                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12198             }
12199             
12200             if(this.labelsm > 0){
12201                 labelCfg.cls += ' col-sm-' + this.labelsm;
12202                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12203             }
12204             
12205             if(this.labelxs > 0){
12206                 labelCfg.cls += ' col-xs-' + this.labelxs;
12207                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12208             }
12209             
12210         } else if ( this.fieldLabel.length) {
12211 //                Roo.log(" label");
12212             cfg.cn = [
12213                 indicator,
12214                {
12215                    tag: 'label',
12216                    //cls : 'input-group-addon',
12217                    html : this.fieldLabel
12218
12219                },
12220
12221                combobox
12222
12223             ];
12224             
12225             if(this.indicatorpos == 'right'){
12226                 
12227                 cfg.cn = [
12228                     {
12229                        tag: 'label',
12230                        cn : [
12231                            {
12232                                tag : 'span',
12233                                html : this.fieldLabel
12234                            },
12235                            indicator
12236                        ]
12237
12238                     },
12239                     combobox
12240
12241                 ];
12242
12243             }
12244
12245         } else {
12246             
12247 //                Roo.log(" no label && no align");
12248                 cfg = combobox
12249                      
12250                 
12251         }
12252         
12253         var settings=this;
12254         ['xs','sm','md','lg'].map(function(size){
12255             if (settings[size]) {
12256                 cfg.cls += ' col-' + size + '-' + settings[size];
12257             }
12258         });
12259         
12260         return cfg;
12261         
12262     },
12263     
12264     
12265     
12266     // private
12267     onResize : function(w, h){
12268 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12269 //        if(typeof w == 'number'){
12270 //            var x = w - this.trigger.getWidth();
12271 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12272 //            this.trigger.setStyle('left', x+'px');
12273 //        }
12274     },
12275
12276     // private
12277     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12278
12279     // private
12280     getResizeEl : function(){
12281         return this.inputEl();
12282     },
12283
12284     // private
12285     getPositionEl : function(){
12286         return this.inputEl();
12287     },
12288
12289     // private
12290     alignErrorIcon : function(){
12291         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12292     },
12293
12294     // private
12295     initEvents : function(){
12296         
12297         this.createList();
12298         
12299         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12300         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12301         if(!this.multiple && this.showToggleBtn){
12302             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12303             if(this.hideTrigger){
12304                 this.trigger.setDisplayed(false);
12305             }
12306             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12307         }
12308         
12309         if(this.multiple){
12310             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12311         }
12312         
12313         if(this.removable && !this.editable && !this.tickable){
12314             var close = this.closeTriggerEl();
12315             
12316             if(close){
12317                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12318                 close.on('click', this.removeBtnClick, this, close);
12319             }
12320         }
12321         
12322         //this.trigger.addClassOnOver('x-form-trigger-over');
12323         //this.trigger.addClassOnClick('x-form-trigger-click');
12324         
12325         //if(!this.width){
12326         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12327         //}
12328     },
12329     
12330     closeTriggerEl : function()
12331     {
12332         var close = this.el.select('.roo-combo-removable-btn', true).first();
12333         return close ? close : false;
12334     },
12335     
12336     removeBtnClick : function(e, h, el)
12337     {
12338         e.preventDefault();
12339         
12340         if(this.fireEvent("remove", this) !== false){
12341             this.reset();
12342             this.fireEvent("afterremove", this)
12343         }
12344     },
12345     
12346     createList : function()
12347     {
12348         this.list = Roo.get(document.body).createChild({
12349             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12350             cls: 'typeahead typeahead-long dropdown-menu shadow',
12351             style: 'display:none'
12352         });
12353         
12354         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12355         
12356     },
12357
12358     // private
12359     initTrigger : function(){
12360        
12361     },
12362
12363     // private
12364     onDestroy : function(){
12365         if(this.trigger){
12366             this.trigger.removeAllListeners();
12367           //  this.trigger.remove();
12368         }
12369         //if(this.wrap){
12370         //    this.wrap.remove();
12371         //}
12372         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12373     },
12374
12375     // private
12376     onFocus : function(){
12377         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12378         /*
12379         if(!this.mimicing){
12380             this.wrap.addClass('x-trigger-wrap-focus');
12381             this.mimicing = true;
12382             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12383             if(this.monitorTab){
12384                 this.el.on("keydown", this.checkTab, this);
12385             }
12386         }
12387         */
12388     },
12389
12390     // private
12391     checkTab : function(e){
12392         if(e.getKey() == e.TAB){
12393             this.triggerBlur();
12394         }
12395     },
12396
12397     // private
12398     onBlur : function(){
12399         // do nothing
12400     },
12401
12402     // private
12403     mimicBlur : function(e, t){
12404         /*
12405         if(!this.wrap.contains(t) && this.validateBlur()){
12406             this.triggerBlur();
12407         }
12408         */
12409     },
12410
12411     // private
12412     triggerBlur : function(){
12413         this.mimicing = false;
12414         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12415         if(this.monitorTab){
12416             this.el.un("keydown", this.checkTab, this);
12417         }
12418         //this.wrap.removeClass('x-trigger-wrap-focus');
12419         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12420     },
12421
12422     // private
12423     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12424     validateBlur : function(e, t){
12425         return true;
12426     },
12427
12428     // private
12429     onDisable : function(){
12430         this.inputEl().dom.disabled = true;
12431         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12432         //if(this.wrap){
12433         //    this.wrap.addClass('x-item-disabled');
12434         //}
12435     },
12436
12437     // private
12438     onEnable : function(){
12439         this.inputEl().dom.disabled = false;
12440         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12441         //if(this.wrap){
12442         //    this.el.removeClass('x-item-disabled');
12443         //}
12444     },
12445
12446     // private
12447     onShow : function(){
12448         var ae = this.getActionEl();
12449         
12450         if(ae){
12451             ae.dom.style.display = '';
12452             ae.dom.style.visibility = 'visible';
12453         }
12454     },
12455
12456     // private
12457     
12458     onHide : function(){
12459         var ae = this.getActionEl();
12460         ae.dom.style.display = 'none';
12461     },
12462
12463     /**
12464      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12465      * by an implementing function.
12466      * @method
12467      * @param {EventObject} e
12468      */
12469     onTriggerClick : Roo.emptyFn
12470 });
12471  
12472 /*
12473 * Licence: LGPL
12474 */
12475
12476 /**
12477  * @class Roo.bootstrap.CardUploader
12478  * @extends Roo.bootstrap.Button
12479  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12480  * @cfg {Number} errorTimeout default 3000
12481  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12482  * @cfg {Array}  html The button text.
12483
12484  *
12485  * @constructor
12486  * Create a new CardUploader
12487  * @param {Object} config The config object
12488  */
12489
12490 Roo.bootstrap.CardUploader = function(config){
12491     
12492  
12493     
12494     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12495     
12496     
12497     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12498         return r.data.id
12499         });
12500     
12501     
12502 };
12503
12504 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12505     
12506      
12507     errorTimeout : 3000,
12508      
12509     images : false,
12510    
12511     fileCollection : false,
12512     allowBlank : true,
12513     
12514     getAutoCreate : function()
12515     {
12516         
12517         var cfg =  {
12518             cls :'form-group' ,
12519             cn : [
12520                
12521                 {
12522                     tag: 'label',
12523                    //cls : 'input-group-addon',
12524                     html : this.fieldLabel
12525
12526                 },
12527
12528                 {
12529                     tag: 'input',
12530                     type : 'hidden',
12531                     value : this.value,
12532                     cls : 'd-none  form-control'
12533                 },
12534                 
12535                 {
12536                     tag: 'input',
12537                     multiple : 'multiple',
12538                     type : 'file',
12539                     cls : 'd-none  roo-card-upload-selector'
12540                 },
12541                 
12542                 {
12543                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12544                 },
12545                 {
12546                     cls : 'card-columns roo-card-uploader-container'
12547                 }
12548
12549             ]
12550         };
12551            
12552          
12553         return cfg;
12554     },
12555     
12556     getChildContainer : function() /// what children are added to.
12557     {
12558         return this.containerEl;
12559     },
12560    
12561     getButtonContainer : function() /// what children are added to.
12562     {
12563         return this.el.select(".roo-card-uploader-button-container").first();
12564     },
12565    
12566     initEvents : function()
12567     {
12568         
12569         Roo.bootstrap.Input.prototype.initEvents.call(this);
12570         
12571         var t = this;
12572         this.addxtype({
12573             xns: Roo.bootstrap,
12574
12575             xtype : 'Button',
12576             container_method : 'getButtonContainer' ,            
12577             html :  this.html, // fix changable?
12578             cls : 'w-100 ',
12579             listeners : {
12580                 'click' : function(btn, e) {
12581                     t.onClick(e);
12582                 }
12583             }
12584         });
12585         
12586         
12587         
12588         
12589         this.urlAPI = (window.createObjectURL && window) || 
12590                                 (window.URL && URL.revokeObjectURL && URL) || 
12591                                 (window.webkitURL && webkitURL);
12592                         
12593          
12594          
12595          
12596         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12597         
12598         this.selectorEl.on('change', this.onFileSelected, this);
12599         if (this.images) {
12600             var t = this;
12601             this.images.forEach(function(img) {
12602                 t.addCard(img)
12603             });
12604             this.images = false;
12605         }
12606         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12607          
12608        
12609     },
12610     
12611    
12612     onClick : function(e)
12613     {
12614         e.preventDefault();
12615          
12616         this.selectorEl.dom.click();
12617          
12618     },
12619     
12620     onFileSelected : function(e)
12621     {
12622         e.preventDefault();
12623         
12624         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12625             return;
12626         }
12627         
12628         Roo.each(this.selectorEl.dom.files, function(file){    
12629             this.addFile(file);
12630         }, this);
12631          
12632     },
12633     
12634       
12635     
12636       
12637     
12638     addFile : function(file)
12639     {
12640            
12641         if(typeof(file) === 'string'){
12642             throw "Add file by name?"; // should not happen
12643             return;
12644         }
12645         
12646         if(!file || !this.urlAPI){
12647             return;
12648         }
12649         
12650         // file;
12651         // file.type;
12652         
12653         var _this = this;
12654         
12655         
12656         var url = _this.urlAPI.createObjectURL( file);
12657            
12658         this.addCard({
12659             id : Roo.bootstrap.CardUploader.ID--,
12660             is_uploaded : false,
12661             src : url,
12662             title : file.name,
12663             mimetype : file.type,
12664             preview : false,
12665             is_deleted : 0
12666         })
12667         
12668     },
12669     
12670     addCard : function (data)
12671     {
12672         // hidden input element?
12673         // if the file is not an image...
12674         //then we need to use something other that and header_image
12675         var t = this;
12676         //   remove.....
12677         var footer = [
12678             {
12679                 xns : Roo.bootstrap,
12680                 xtype : 'CardFooter',
12681                 items: [
12682                     {
12683                         xns : Roo.bootstrap,
12684                         xtype : 'Element',
12685                         cls : 'd-flex',
12686                         items : [
12687                             
12688                             {
12689                                 xns : Roo.bootstrap,
12690                                 xtype : 'Button',
12691                                 html : String.format("<small>{0}</small>", data.title),
12692                                 cls : 'col-11 text-left',
12693                                 size: 'sm',
12694                                 weight: 'link',
12695                                 fa : 'download',
12696                                 listeners : {
12697                                     click : function() {
12698                                         this.downloadCard(data.id)
12699                                     }
12700                                 }
12701                             },
12702                           
12703                             {
12704                                 xns : Roo.bootstrap,
12705                                 xtype : 'Button',
12706                                 
12707                                 size : 'sm',
12708                                 weight: 'danger',
12709                                 cls : 'col-1',
12710                                 fa : 'times',
12711                                 listeners : {
12712                                     click : function() {
12713                                         t.removeCard(data.id)
12714                                     }
12715                                 }
12716                             }
12717                         ]
12718                     }
12719                     
12720                 ] 
12721             }
12722             
12723         ];
12724
12725         var cn = this.addxtype(
12726             {
12727                  
12728                 xns : Roo.bootstrap,
12729                 xtype : 'Card',
12730                 closeable : true,
12731                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12732                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12733                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12734                 data : data,
12735                 html : false,
12736                  
12737                 items : footer,
12738                 initEvents : function() {
12739                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12740                     this.imgEl = this.el.select('.card-img-top').first();
12741                     if (this.imgEl) {
12742                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12743                         this.imgEl.set({ 'pointer' : 'cursor' });
12744                                   
12745                     }
12746                     
12747                   
12748                 }
12749                 
12750             }
12751         );
12752         // dont' really need ot update items.
12753         // this.items.push(cn);
12754         this.fileCollection.add(cn);
12755         this.updateInput();
12756         
12757     },
12758     removeCard : function(id)
12759     {
12760         
12761         var card  = this.fileCollection.get(id);
12762         card.data.is_deleted = 1;
12763         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12764         this.fileCollection.remove(card);
12765         //this.items = this.items.filter(function(e) { return e != card });
12766         // dont' really need ot update items.
12767         card.el.dom.parentNode.removeChild(card.el.dom);
12768         
12769     },
12770     reset: function()
12771     {
12772         this.fileCollection.each(function(card) {
12773             card.el.dom.parentNode.removeChild(card.el.dom);    
12774         });
12775         this.fileCollection.clear();
12776         this.updateInput();
12777     },
12778     
12779     updateInput : function()
12780     {
12781         var data = [];
12782         this.fileCollection.each(function(e) {
12783             data.push(e.data);
12784         });
12785         
12786         this.inputEl().dom.value = JSON.stringify(data);
12787     }
12788     
12789     
12790 });
12791
12792
12793 Roo.bootstrap.CardUploader.ID = -1;/*
12794  * Based on:
12795  * Ext JS Library 1.1.1
12796  * Copyright(c) 2006-2007, Ext JS, LLC.
12797  *
12798  * Originally Released Under LGPL - original licence link has changed is not relivant.
12799  *
12800  * Fork - LGPL
12801  * <script type="text/javascript">
12802  */
12803
12804
12805 /**
12806  * @class Roo.data.SortTypes
12807  * @singleton
12808  * Defines the default sorting (casting?) comparison functions used when sorting data.
12809  */
12810 Roo.data.SortTypes = {
12811     /**
12812      * Default sort that does nothing
12813      * @param {Mixed} s The value being converted
12814      * @return {Mixed} The comparison value
12815      */
12816     none : function(s){
12817         return s;
12818     },
12819     
12820     /**
12821      * The regular expression used to strip tags
12822      * @type {RegExp}
12823      * @property
12824      */
12825     stripTagsRE : /<\/?[^>]+>/gi,
12826     
12827     /**
12828      * Strips all HTML tags to sort on text only
12829      * @param {Mixed} s The value being converted
12830      * @return {String} The comparison value
12831      */
12832     asText : function(s){
12833         return String(s).replace(this.stripTagsRE, "");
12834     },
12835     
12836     /**
12837      * Strips all HTML tags to sort on text only - Case insensitive
12838      * @param {Mixed} s The value being converted
12839      * @return {String} The comparison value
12840      */
12841     asUCText : function(s){
12842         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12843     },
12844     
12845     /**
12846      * Case insensitive string
12847      * @param {Mixed} s The value being converted
12848      * @return {String} The comparison value
12849      */
12850     asUCString : function(s) {
12851         return String(s).toUpperCase();
12852     },
12853     
12854     /**
12855      * Date sorting
12856      * @param {Mixed} s The value being converted
12857      * @return {Number} The comparison value
12858      */
12859     asDate : function(s) {
12860         if(!s){
12861             return 0;
12862         }
12863         if(s instanceof Date){
12864             return s.getTime();
12865         }
12866         return Date.parse(String(s));
12867     },
12868     
12869     /**
12870      * Float sorting
12871      * @param {Mixed} s The value being converted
12872      * @return {Float} The comparison value
12873      */
12874     asFloat : function(s) {
12875         var val = parseFloat(String(s).replace(/,/g, ""));
12876         if(isNaN(val)) {
12877             val = 0;
12878         }
12879         return val;
12880     },
12881     
12882     /**
12883      * Integer sorting
12884      * @param {Mixed} s The value being converted
12885      * @return {Number} The comparison value
12886      */
12887     asInt : function(s) {
12888         var val = parseInt(String(s).replace(/,/g, ""));
12889         if(isNaN(val)) {
12890             val = 0;
12891         }
12892         return val;
12893     }
12894 };/*
12895  * Based on:
12896  * Ext JS Library 1.1.1
12897  * Copyright(c) 2006-2007, Ext JS, LLC.
12898  *
12899  * Originally Released Under LGPL - original licence link has changed is not relivant.
12900  *
12901  * Fork - LGPL
12902  * <script type="text/javascript">
12903  */
12904
12905 /**
12906 * @class Roo.data.Record
12907  * Instances of this class encapsulate both record <em>definition</em> information, and record
12908  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12909  * to access Records cached in an {@link Roo.data.Store} object.<br>
12910  * <p>
12911  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12912  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12913  * objects.<br>
12914  * <p>
12915  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12916  * @constructor
12917  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12918  * {@link #create}. The parameters are the same.
12919  * @param {Array} data An associative Array of data values keyed by the field name.
12920  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12921  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12922  * not specified an integer id is generated.
12923  */
12924 Roo.data.Record = function(data, id){
12925     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12926     this.data = data;
12927 };
12928
12929 /**
12930  * Generate a constructor for a specific record layout.
12931  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12932  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12933  * Each field definition object may contain the following properties: <ul>
12934  * <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,
12935  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12936  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12937  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12938  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12939  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12940  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12941  * this may be omitted.</p></li>
12942  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12943  * <ul><li>auto (Default, implies no conversion)</li>
12944  * <li>string</li>
12945  * <li>int</li>
12946  * <li>float</li>
12947  * <li>boolean</li>
12948  * <li>date</li></ul></p></li>
12949  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12950  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12951  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12952  * by the Reader into an object that will be stored in the Record. It is passed the
12953  * following parameters:<ul>
12954  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12955  * </ul></p></li>
12956  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12957  * </ul>
12958  * <br>usage:<br><pre><code>
12959 var TopicRecord = Roo.data.Record.create(
12960     {name: 'title', mapping: 'topic_title'},
12961     {name: 'author', mapping: 'username'},
12962     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12963     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12964     {name: 'lastPoster', mapping: 'user2'},
12965     {name: 'excerpt', mapping: 'post_text'}
12966 );
12967
12968 var myNewRecord = new TopicRecord({
12969     title: 'Do my job please',
12970     author: 'noobie',
12971     totalPosts: 1,
12972     lastPost: new Date(),
12973     lastPoster: 'Animal',
12974     excerpt: 'No way dude!'
12975 });
12976 myStore.add(myNewRecord);
12977 </code></pre>
12978  * @method create
12979  * @static
12980  */
12981 Roo.data.Record.create = function(o){
12982     var f = function(){
12983         f.superclass.constructor.apply(this, arguments);
12984     };
12985     Roo.extend(f, Roo.data.Record);
12986     var p = f.prototype;
12987     p.fields = new Roo.util.MixedCollection(false, function(field){
12988         return field.name;
12989     });
12990     for(var i = 0, len = o.length; i < len; i++){
12991         p.fields.add(new Roo.data.Field(o[i]));
12992     }
12993     f.getField = function(name){
12994         return p.fields.get(name);  
12995     };
12996     return f;
12997 };
12998
12999 Roo.data.Record.AUTO_ID = 1000;
13000 Roo.data.Record.EDIT = 'edit';
13001 Roo.data.Record.REJECT = 'reject';
13002 Roo.data.Record.COMMIT = 'commit';
13003
13004 Roo.data.Record.prototype = {
13005     /**
13006      * Readonly flag - true if this record has been modified.
13007      * @type Boolean
13008      */
13009     dirty : false,
13010     editing : false,
13011     error: null,
13012     modified: null,
13013
13014     // private
13015     join : function(store){
13016         this.store = store;
13017     },
13018
13019     /**
13020      * Set the named field to the specified value.
13021      * @param {String} name The name of the field to set.
13022      * @param {Object} value The value to set the field to.
13023      */
13024     set : function(name, value){
13025         if(this.data[name] == value){
13026             return;
13027         }
13028         this.dirty = true;
13029         if(!this.modified){
13030             this.modified = {};
13031         }
13032         if(typeof this.modified[name] == 'undefined'){
13033             this.modified[name] = this.data[name];
13034         }
13035         this.data[name] = value;
13036         if(!this.editing && this.store){
13037             this.store.afterEdit(this);
13038         }       
13039     },
13040
13041     /**
13042      * Get the value of the named field.
13043      * @param {String} name The name of the field to get the value of.
13044      * @return {Object} The value of the field.
13045      */
13046     get : function(name){
13047         return this.data[name]; 
13048     },
13049
13050     // private
13051     beginEdit : function(){
13052         this.editing = true;
13053         this.modified = {}; 
13054     },
13055
13056     // private
13057     cancelEdit : function(){
13058         this.editing = false;
13059         delete this.modified;
13060     },
13061
13062     // private
13063     endEdit : function(){
13064         this.editing = false;
13065         if(this.dirty && this.store){
13066             this.store.afterEdit(this);
13067         }
13068     },
13069
13070     /**
13071      * Usually called by the {@link Roo.data.Store} which owns the Record.
13072      * Rejects all changes made to the Record since either creation, or the last commit operation.
13073      * Modified fields are reverted to their original values.
13074      * <p>
13075      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13076      * of reject operations.
13077      */
13078     reject : function(){
13079         var m = this.modified;
13080         for(var n in m){
13081             if(typeof m[n] != "function"){
13082                 this.data[n] = m[n];
13083             }
13084         }
13085         this.dirty = false;
13086         delete this.modified;
13087         this.editing = false;
13088         if(this.store){
13089             this.store.afterReject(this);
13090         }
13091     },
13092
13093     /**
13094      * Usually called by the {@link Roo.data.Store} which owns the Record.
13095      * Commits all changes made to the Record since either creation, or the last commit operation.
13096      * <p>
13097      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13098      * of commit operations.
13099      */
13100     commit : function(){
13101         this.dirty = false;
13102         delete this.modified;
13103         this.editing = false;
13104         if(this.store){
13105             this.store.afterCommit(this);
13106         }
13107     },
13108
13109     // private
13110     hasError : function(){
13111         return this.error != null;
13112     },
13113
13114     // private
13115     clearError : function(){
13116         this.error = null;
13117     },
13118
13119     /**
13120      * Creates a copy of this record.
13121      * @param {String} id (optional) A new record id if you don't want to use this record's id
13122      * @return {Record}
13123      */
13124     copy : function(newId) {
13125         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13126     }
13127 };/*
13128  * Based on:
13129  * Ext JS Library 1.1.1
13130  * Copyright(c) 2006-2007, Ext JS, LLC.
13131  *
13132  * Originally Released Under LGPL - original licence link has changed is not relivant.
13133  *
13134  * Fork - LGPL
13135  * <script type="text/javascript">
13136  */
13137
13138
13139
13140 /**
13141  * @class Roo.data.Store
13142  * @extends Roo.util.Observable
13143  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13144  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13145  * <p>
13146  * 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
13147  * has no knowledge of the format of the data returned by the Proxy.<br>
13148  * <p>
13149  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13150  * instances from the data object. These records are cached and made available through accessor functions.
13151  * @constructor
13152  * Creates a new Store.
13153  * @param {Object} config A config object containing the objects needed for the Store to access data,
13154  * and read the data into Records.
13155  */
13156 Roo.data.Store = function(config){
13157     this.data = new Roo.util.MixedCollection(false);
13158     this.data.getKey = function(o){
13159         return o.id;
13160     };
13161     this.baseParams = {};
13162     // private
13163     this.paramNames = {
13164         "start" : "start",
13165         "limit" : "limit",
13166         "sort" : "sort",
13167         "dir" : "dir",
13168         "multisort" : "_multisort"
13169     };
13170
13171     if(config && config.data){
13172         this.inlineData = config.data;
13173         delete config.data;
13174     }
13175
13176     Roo.apply(this, config);
13177     
13178     if(this.reader){ // reader passed
13179         this.reader = Roo.factory(this.reader, Roo.data);
13180         this.reader.xmodule = this.xmodule || false;
13181         if(!this.recordType){
13182             this.recordType = this.reader.recordType;
13183         }
13184         if(this.reader.onMetaChange){
13185             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13186         }
13187     }
13188
13189     if(this.recordType){
13190         this.fields = this.recordType.prototype.fields;
13191     }
13192     this.modified = [];
13193
13194     this.addEvents({
13195         /**
13196          * @event datachanged
13197          * Fires when the data cache has changed, and a widget which is using this Store
13198          * as a Record cache should refresh its view.
13199          * @param {Store} this
13200          */
13201         datachanged : true,
13202         /**
13203          * @event metachange
13204          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13205          * @param {Store} this
13206          * @param {Object} meta The JSON metadata
13207          */
13208         metachange : true,
13209         /**
13210          * @event add
13211          * Fires when Records have been added to the Store
13212          * @param {Store} this
13213          * @param {Roo.data.Record[]} records The array of Records added
13214          * @param {Number} index The index at which the record(s) were added
13215          */
13216         add : true,
13217         /**
13218          * @event remove
13219          * Fires when a Record has been removed from the Store
13220          * @param {Store} this
13221          * @param {Roo.data.Record} record The Record that was removed
13222          * @param {Number} index The index at which the record was removed
13223          */
13224         remove : true,
13225         /**
13226          * @event update
13227          * Fires when a Record has been updated
13228          * @param {Store} this
13229          * @param {Roo.data.Record} record The Record that was updated
13230          * @param {String} operation The update operation being performed.  Value may be one of:
13231          * <pre><code>
13232  Roo.data.Record.EDIT
13233  Roo.data.Record.REJECT
13234  Roo.data.Record.COMMIT
13235          * </code></pre>
13236          */
13237         update : true,
13238         /**
13239          * @event clear
13240          * Fires when the data cache has been cleared.
13241          * @param {Store} this
13242          */
13243         clear : true,
13244         /**
13245          * @event beforeload
13246          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13247          * the load action will be canceled.
13248          * @param {Store} this
13249          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13250          */
13251         beforeload : true,
13252         /**
13253          * @event beforeloadadd
13254          * Fires after a new set of Records has been loaded.
13255          * @param {Store} this
13256          * @param {Roo.data.Record[]} records The Records that were loaded
13257          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13258          */
13259         beforeloadadd : true,
13260         /**
13261          * @event load
13262          * Fires after a new set of Records has been loaded, before they are added to the store.
13263          * @param {Store} this
13264          * @param {Roo.data.Record[]} records The Records that were loaded
13265          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13266          * @params {Object} return from reader
13267          */
13268         load : true,
13269         /**
13270          * @event loadexception
13271          * Fires if an exception occurs in the Proxy during loading.
13272          * Called with the signature of the Proxy's "loadexception" event.
13273          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13274          * 
13275          * @param {Proxy} 
13276          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13277          * @param {Object} load options 
13278          * @param {Object} jsonData from your request (normally this contains the Exception)
13279          */
13280         loadexception : true
13281     });
13282     
13283     if(this.proxy){
13284         this.proxy = Roo.factory(this.proxy, Roo.data);
13285         this.proxy.xmodule = this.xmodule || false;
13286         this.relayEvents(this.proxy,  ["loadexception"]);
13287     }
13288     this.sortToggle = {};
13289     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13290
13291     Roo.data.Store.superclass.constructor.call(this);
13292
13293     if(this.inlineData){
13294         this.loadData(this.inlineData);
13295         delete this.inlineData;
13296     }
13297 };
13298
13299 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13300      /**
13301     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13302     * without a remote query - used by combo/forms at present.
13303     */
13304     
13305     /**
13306     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13307     */
13308     /**
13309     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13310     */
13311     /**
13312     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13313     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13314     */
13315     /**
13316     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13317     * on any HTTP request
13318     */
13319     /**
13320     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13321     */
13322     /**
13323     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13324     */
13325     multiSort: false,
13326     /**
13327     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13328     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13329     */
13330     remoteSort : false,
13331
13332     /**
13333     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13334      * loaded or when a record is removed. (defaults to false).
13335     */
13336     pruneModifiedRecords : false,
13337
13338     // private
13339     lastOptions : null,
13340
13341     /**
13342      * Add Records to the Store and fires the add event.
13343      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13344      */
13345     add : function(records){
13346         records = [].concat(records);
13347         for(var i = 0, len = records.length; i < len; i++){
13348             records[i].join(this);
13349         }
13350         var index = this.data.length;
13351         this.data.addAll(records);
13352         this.fireEvent("add", this, records, index);
13353     },
13354
13355     /**
13356      * Remove a Record from the Store and fires the remove event.
13357      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13358      */
13359     remove : function(record){
13360         var index = this.data.indexOf(record);
13361         this.data.removeAt(index);
13362  
13363         if(this.pruneModifiedRecords){
13364             this.modified.remove(record);
13365         }
13366         this.fireEvent("remove", this, record, index);
13367     },
13368
13369     /**
13370      * Remove all Records from the Store and fires the clear event.
13371      */
13372     removeAll : function(){
13373         this.data.clear();
13374         if(this.pruneModifiedRecords){
13375             this.modified = [];
13376         }
13377         this.fireEvent("clear", this);
13378     },
13379
13380     /**
13381      * Inserts Records to the Store at the given index and fires the add event.
13382      * @param {Number} index The start index at which to insert the passed Records.
13383      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13384      */
13385     insert : function(index, records){
13386         records = [].concat(records);
13387         for(var i = 0, len = records.length; i < len; i++){
13388             this.data.insert(index, records[i]);
13389             records[i].join(this);
13390         }
13391         this.fireEvent("add", this, records, index);
13392     },
13393
13394     /**
13395      * Get the index within the cache of the passed Record.
13396      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13397      * @return {Number} The index of the passed Record. Returns -1 if not found.
13398      */
13399     indexOf : function(record){
13400         return this.data.indexOf(record);
13401     },
13402
13403     /**
13404      * Get the index within the cache of the Record with the passed id.
13405      * @param {String} id The id of the Record to find.
13406      * @return {Number} The index of the Record. Returns -1 if not found.
13407      */
13408     indexOfId : function(id){
13409         return this.data.indexOfKey(id);
13410     },
13411
13412     /**
13413      * Get the Record with the specified id.
13414      * @param {String} id The id of the Record to find.
13415      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13416      */
13417     getById : function(id){
13418         return this.data.key(id);
13419     },
13420
13421     /**
13422      * Get the Record at the specified index.
13423      * @param {Number} index The index of the Record to find.
13424      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13425      */
13426     getAt : function(index){
13427         return this.data.itemAt(index);
13428     },
13429
13430     /**
13431      * Returns a range of Records between specified indices.
13432      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13433      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13434      * @return {Roo.data.Record[]} An array of Records
13435      */
13436     getRange : function(start, end){
13437         return this.data.getRange(start, end);
13438     },
13439
13440     // private
13441     storeOptions : function(o){
13442         o = Roo.apply({}, o);
13443         delete o.callback;
13444         delete o.scope;
13445         this.lastOptions = o;
13446     },
13447
13448     /**
13449      * Loads the Record cache from the configured Proxy using the configured Reader.
13450      * <p>
13451      * If using remote paging, then the first load call must specify the <em>start</em>
13452      * and <em>limit</em> properties in the options.params property to establish the initial
13453      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13454      * <p>
13455      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13456      * and this call will return before the new data has been loaded. Perform any post-processing
13457      * in a callback function, or in a "load" event handler.</strong>
13458      * <p>
13459      * @param {Object} options An object containing properties which control loading options:<ul>
13460      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13461      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13462      * passed the following arguments:<ul>
13463      * <li>r : Roo.data.Record[]</li>
13464      * <li>options: Options object from the load call</li>
13465      * <li>success: Boolean success indicator</li></ul></li>
13466      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13467      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13468      * </ul>
13469      */
13470     load : function(options){
13471         options = options || {};
13472         if(this.fireEvent("beforeload", this, options) !== false){
13473             this.storeOptions(options);
13474             var p = Roo.apply(options.params || {}, this.baseParams);
13475             // if meta was not loaded from remote source.. try requesting it.
13476             if (!this.reader.metaFromRemote) {
13477                 p._requestMeta = 1;
13478             }
13479             if(this.sortInfo && this.remoteSort){
13480                 var pn = this.paramNames;
13481                 p[pn["sort"]] = this.sortInfo.field;
13482                 p[pn["dir"]] = this.sortInfo.direction;
13483             }
13484             if (this.multiSort) {
13485                 var pn = this.paramNames;
13486                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13487             }
13488             
13489             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13490         }
13491     },
13492
13493     /**
13494      * Reloads the Record cache from the configured Proxy using the configured Reader and
13495      * the options from the last load operation performed.
13496      * @param {Object} options (optional) An object containing properties which may override the options
13497      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13498      * the most recently used options are reused).
13499      */
13500     reload : function(options){
13501         this.load(Roo.applyIf(options||{}, this.lastOptions));
13502     },
13503
13504     // private
13505     // Called as a callback by the Reader during a load operation.
13506     loadRecords : function(o, options, success){
13507         if(!o || success === false){
13508             if(success !== false){
13509                 this.fireEvent("load", this, [], options, o);
13510             }
13511             if(options.callback){
13512                 options.callback.call(options.scope || this, [], options, false);
13513             }
13514             return;
13515         }
13516         // if data returned failure - throw an exception.
13517         if (o.success === false) {
13518             // show a message if no listener is registered.
13519             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13520                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13521             }
13522             // loadmask wil be hooked into this..
13523             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13524             return;
13525         }
13526         var r = o.records, t = o.totalRecords || r.length;
13527         
13528         this.fireEvent("beforeloadadd", this, r, options, o);
13529         
13530         if(!options || options.add !== true){
13531             if(this.pruneModifiedRecords){
13532                 this.modified = [];
13533             }
13534             for(var i = 0, len = r.length; i < len; i++){
13535                 r[i].join(this);
13536             }
13537             if(this.snapshot){
13538                 this.data = this.snapshot;
13539                 delete this.snapshot;
13540             }
13541             this.data.clear();
13542             this.data.addAll(r);
13543             this.totalLength = t;
13544             this.applySort();
13545             this.fireEvent("datachanged", this);
13546         }else{
13547             this.totalLength = Math.max(t, this.data.length+r.length);
13548             this.add(r);
13549         }
13550         
13551         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13552                 
13553             var e = new Roo.data.Record({});
13554
13555             e.set(this.parent.displayField, this.parent.emptyTitle);
13556             e.set(this.parent.valueField, '');
13557
13558             this.insert(0, e);
13559         }
13560             
13561         this.fireEvent("load", this, r, options, o);
13562         if(options.callback){
13563             options.callback.call(options.scope || this, r, options, true);
13564         }
13565     },
13566
13567
13568     /**
13569      * Loads data from a passed data block. A Reader which understands the format of the data
13570      * must have been configured in the constructor.
13571      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13572      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13573      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13574      */
13575     loadData : function(o, append){
13576         var r = this.reader.readRecords(o);
13577         this.loadRecords(r, {add: append}, true);
13578     },
13579     
13580      /**
13581      * using 'cn' the nested child reader read the child array into it's child stores.
13582      * @param {Object} rec The record with a 'children array
13583      */
13584     loadDataFromChildren : function(rec)
13585     {
13586         this.loadData(this.reader.toLoadData(rec));
13587     },
13588     
13589
13590     /**
13591      * Gets the number of cached records.
13592      * <p>
13593      * <em>If using paging, this may not be the total size of the dataset. If the data object
13594      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13595      * the data set size</em>
13596      */
13597     getCount : function(){
13598         return this.data.length || 0;
13599     },
13600
13601     /**
13602      * Gets the total number of records in the dataset as returned by the server.
13603      * <p>
13604      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13605      * the dataset size</em>
13606      */
13607     getTotalCount : function(){
13608         return this.totalLength || 0;
13609     },
13610
13611     /**
13612      * Returns the sort state of the Store as an object with two properties:
13613      * <pre><code>
13614  field {String} The name of the field by which the Records are sorted
13615  direction {String} The sort order, "ASC" or "DESC"
13616      * </code></pre>
13617      */
13618     getSortState : function(){
13619         return this.sortInfo;
13620     },
13621
13622     // private
13623     applySort : function(){
13624         if(this.sortInfo && !this.remoteSort){
13625             var s = this.sortInfo, f = s.field;
13626             var st = this.fields.get(f).sortType;
13627             var fn = function(r1, r2){
13628                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13629                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13630             };
13631             this.data.sort(s.direction, fn);
13632             if(this.snapshot && this.snapshot != this.data){
13633                 this.snapshot.sort(s.direction, fn);
13634             }
13635         }
13636     },
13637
13638     /**
13639      * Sets the default sort column and order to be used by the next load operation.
13640      * @param {String} fieldName The name of the field to sort by.
13641      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13642      */
13643     setDefaultSort : function(field, dir){
13644         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13645     },
13646
13647     /**
13648      * Sort the Records.
13649      * If remote sorting is used, the sort is performed on the server, and the cache is
13650      * reloaded. If local sorting is used, the cache is sorted internally.
13651      * @param {String} fieldName The name of the field to sort by.
13652      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13653      */
13654     sort : function(fieldName, dir){
13655         var f = this.fields.get(fieldName);
13656         if(!dir){
13657             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13658             
13659             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13660                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13661             }else{
13662                 dir = f.sortDir;
13663             }
13664         }
13665         this.sortToggle[f.name] = dir;
13666         this.sortInfo = {field: f.name, direction: dir};
13667         if(!this.remoteSort){
13668             this.applySort();
13669             this.fireEvent("datachanged", this);
13670         }else{
13671             this.load(this.lastOptions);
13672         }
13673     },
13674
13675     /**
13676      * Calls the specified function for each of the Records in the cache.
13677      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13678      * Returning <em>false</em> aborts and exits the iteration.
13679      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13680      */
13681     each : function(fn, scope){
13682         this.data.each(fn, scope);
13683     },
13684
13685     /**
13686      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13687      * (e.g., during paging).
13688      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13689      */
13690     getModifiedRecords : function(){
13691         return this.modified;
13692     },
13693
13694     // private
13695     createFilterFn : function(property, value, anyMatch){
13696         if(!value.exec){ // not a regex
13697             value = String(value);
13698             if(value.length == 0){
13699                 return false;
13700             }
13701             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13702         }
13703         return function(r){
13704             return value.test(r.data[property]);
13705         };
13706     },
13707
13708     /**
13709      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13710      * @param {String} property A field on your records
13711      * @param {Number} start The record index to start at (defaults to 0)
13712      * @param {Number} end The last record index to include (defaults to length - 1)
13713      * @return {Number} The sum
13714      */
13715     sum : function(property, start, end){
13716         var rs = this.data.items, v = 0;
13717         start = start || 0;
13718         end = (end || end === 0) ? end : rs.length-1;
13719
13720         for(var i = start; i <= end; i++){
13721             v += (rs[i].data[property] || 0);
13722         }
13723         return v;
13724     },
13725
13726     /**
13727      * Filter the records by a specified property.
13728      * @param {String} field A field on your records
13729      * @param {String/RegExp} value Either a string that the field
13730      * should start with or a RegExp to test against the field
13731      * @param {Boolean} anyMatch True to match any part not just the beginning
13732      */
13733     filter : function(property, value, anyMatch){
13734         var fn = this.createFilterFn(property, value, anyMatch);
13735         return fn ? this.filterBy(fn) : this.clearFilter();
13736     },
13737
13738     /**
13739      * Filter by a function. The specified function will be called with each
13740      * record in this data source. If the function returns true the record is included,
13741      * otherwise it is filtered.
13742      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13743      * @param {Object} scope (optional) The scope of the function (defaults to this)
13744      */
13745     filterBy : function(fn, scope){
13746         this.snapshot = this.snapshot || this.data;
13747         this.data = this.queryBy(fn, scope||this);
13748         this.fireEvent("datachanged", this);
13749     },
13750
13751     /**
13752      * Query the records by a specified property.
13753      * @param {String} field A field on your records
13754      * @param {String/RegExp} value Either a string that the field
13755      * should start with or a RegExp to test against the field
13756      * @param {Boolean} anyMatch True to match any part not just the beginning
13757      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13758      */
13759     query : function(property, value, anyMatch){
13760         var fn = this.createFilterFn(property, value, anyMatch);
13761         return fn ? this.queryBy(fn) : this.data.clone();
13762     },
13763
13764     /**
13765      * Query by a function. The specified function will be called with each
13766      * record in this data source. If the function returns true the record is included
13767      * in the results.
13768      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13769      * @param {Object} scope (optional) The scope of the function (defaults to this)
13770       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13771      **/
13772     queryBy : function(fn, scope){
13773         var data = this.snapshot || this.data;
13774         return data.filterBy(fn, scope||this);
13775     },
13776
13777     /**
13778      * Collects unique values for a particular dataIndex from this store.
13779      * @param {String} dataIndex The property to collect
13780      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13781      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13782      * @return {Array} An array of the unique values
13783      **/
13784     collect : function(dataIndex, allowNull, bypassFilter){
13785         var d = (bypassFilter === true && this.snapshot) ?
13786                 this.snapshot.items : this.data.items;
13787         var v, sv, r = [], l = {};
13788         for(var i = 0, len = d.length; i < len; i++){
13789             v = d[i].data[dataIndex];
13790             sv = String(v);
13791             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13792                 l[sv] = true;
13793                 r[r.length] = v;
13794             }
13795         }
13796         return r;
13797     },
13798
13799     /**
13800      * Revert to a view of the Record cache with no filtering applied.
13801      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13802      */
13803     clearFilter : function(suppressEvent){
13804         if(this.snapshot && this.snapshot != this.data){
13805             this.data = this.snapshot;
13806             delete this.snapshot;
13807             if(suppressEvent !== true){
13808                 this.fireEvent("datachanged", this);
13809             }
13810         }
13811     },
13812
13813     // private
13814     afterEdit : function(record){
13815         if(this.modified.indexOf(record) == -1){
13816             this.modified.push(record);
13817         }
13818         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13819     },
13820     
13821     // private
13822     afterReject : function(record){
13823         this.modified.remove(record);
13824         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13825     },
13826
13827     // private
13828     afterCommit : function(record){
13829         this.modified.remove(record);
13830         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13831     },
13832
13833     /**
13834      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13835      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13836      */
13837     commitChanges : function(){
13838         var m = this.modified.slice(0);
13839         this.modified = [];
13840         for(var i = 0, len = m.length; i < len; i++){
13841             m[i].commit();
13842         }
13843     },
13844
13845     /**
13846      * Cancel outstanding changes on all changed records.
13847      */
13848     rejectChanges : function(){
13849         var m = this.modified.slice(0);
13850         this.modified = [];
13851         for(var i = 0, len = m.length; i < len; i++){
13852             m[i].reject();
13853         }
13854     },
13855
13856     onMetaChange : function(meta, rtype, o){
13857         this.recordType = rtype;
13858         this.fields = rtype.prototype.fields;
13859         delete this.snapshot;
13860         this.sortInfo = meta.sortInfo || this.sortInfo;
13861         this.modified = [];
13862         this.fireEvent('metachange', this, this.reader.meta);
13863     },
13864     
13865     moveIndex : function(data, type)
13866     {
13867         var index = this.indexOf(data);
13868         
13869         var newIndex = index + type;
13870         
13871         this.remove(data);
13872         
13873         this.insert(newIndex, data);
13874         
13875     }
13876 });/*
13877  * Based on:
13878  * Ext JS Library 1.1.1
13879  * Copyright(c) 2006-2007, Ext JS, LLC.
13880  *
13881  * Originally Released Under LGPL - original licence link has changed is not relivant.
13882  *
13883  * Fork - LGPL
13884  * <script type="text/javascript">
13885  */
13886
13887 /**
13888  * @class Roo.data.SimpleStore
13889  * @extends Roo.data.Store
13890  * Small helper class to make creating Stores from Array data easier.
13891  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13892  * @cfg {Array} fields An array of field definition objects, or field name strings.
13893  * @cfg {Object} an existing reader (eg. copied from another store)
13894  * @cfg {Array} data The multi-dimensional array of data
13895  * @constructor
13896  * @param {Object} config
13897  */
13898 Roo.data.SimpleStore = function(config)
13899 {
13900     Roo.data.SimpleStore.superclass.constructor.call(this, {
13901         isLocal : true,
13902         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13903                 id: config.id
13904             },
13905             Roo.data.Record.create(config.fields)
13906         ),
13907         proxy : new Roo.data.MemoryProxy(config.data)
13908     });
13909     this.load();
13910 };
13911 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13912  * Based on:
13913  * Ext JS Library 1.1.1
13914  * Copyright(c) 2006-2007, Ext JS, LLC.
13915  *
13916  * Originally Released Under LGPL - original licence link has changed is not relivant.
13917  *
13918  * Fork - LGPL
13919  * <script type="text/javascript">
13920  */
13921
13922 /**
13923 /**
13924  * @extends Roo.data.Store
13925  * @class Roo.data.JsonStore
13926  * Small helper class to make creating Stores for JSON data easier. <br/>
13927 <pre><code>
13928 var store = new Roo.data.JsonStore({
13929     url: 'get-images.php',
13930     root: 'images',
13931     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13932 });
13933 </code></pre>
13934  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13935  * JsonReader and HttpProxy (unless inline data is provided).</b>
13936  * @cfg {Array} fields An array of field definition objects, or field name strings.
13937  * @constructor
13938  * @param {Object} config
13939  */
13940 Roo.data.JsonStore = function(c){
13941     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13942         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13943         reader: new Roo.data.JsonReader(c, c.fields)
13944     }));
13945 };
13946 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13947  * Based on:
13948  * Ext JS Library 1.1.1
13949  * Copyright(c) 2006-2007, Ext JS, LLC.
13950  *
13951  * Originally Released Under LGPL - original licence link has changed is not relivant.
13952  *
13953  * Fork - LGPL
13954  * <script type="text/javascript">
13955  */
13956
13957  
13958 Roo.data.Field = function(config){
13959     if(typeof config == "string"){
13960         config = {name: config};
13961     }
13962     Roo.apply(this, config);
13963     
13964     if(!this.type){
13965         this.type = "auto";
13966     }
13967     
13968     var st = Roo.data.SortTypes;
13969     // named sortTypes are supported, here we look them up
13970     if(typeof this.sortType == "string"){
13971         this.sortType = st[this.sortType];
13972     }
13973     
13974     // set default sortType for strings and dates
13975     if(!this.sortType){
13976         switch(this.type){
13977             case "string":
13978                 this.sortType = st.asUCString;
13979                 break;
13980             case "date":
13981                 this.sortType = st.asDate;
13982                 break;
13983             default:
13984                 this.sortType = st.none;
13985         }
13986     }
13987
13988     // define once
13989     var stripRe = /[\$,%]/g;
13990
13991     // prebuilt conversion function for this field, instead of
13992     // switching every time we're reading a value
13993     if(!this.convert){
13994         var cv, dateFormat = this.dateFormat;
13995         switch(this.type){
13996             case "":
13997             case "auto":
13998             case undefined:
13999                 cv = function(v){ return v; };
14000                 break;
14001             case "string":
14002                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14003                 break;
14004             case "int":
14005                 cv = function(v){
14006                     return v !== undefined && v !== null && v !== '' ?
14007                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14008                     };
14009                 break;
14010             case "float":
14011                 cv = function(v){
14012                     return v !== undefined && v !== null && v !== '' ?
14013                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14014                     };
14015                 break;
14016             case "bool":
14017             case "boolean":
14018                 cv = function(v){ return v === true || v === "true" || v == 1; };
14019                 break;
14020             case "date":
14021                 cv = function(v){
14022                     if(!v){
14023                         return '';
14024                     }
14025                     if(v instanceof Date){
14026                         return v;
14027                     }
14028                     if(dateFormat){
14029                         if(dateFormat == "timestamp"){
14030                             return new Date(v*1000);
14031                         }
14032                         return Date.parseDate(v, dateFormat);
14033                     }
14034                     var parsed = Date.parse(v);
14035                     return parsed ? new Date(parsed) : null;
14036                 };
14037              break;
14038             
14039         }
14040         this.convert = cv;
14041     }
14042 };
14043
14044 Roo.data.Field.prototype = {
14045     dateFormat: null,
14046     defaultValue: "",
14047     mapping: null,
14048     sortType : null,
14049     sortDir : "ASC"
14050 };/*
14051  * Based on:
14052  * Ext JS Library 1.1.1
14053  * Copyright(c) 2006-2007, Ext JS, LLC.
14054  *
14055  * Originally Released Under LGPL - original licence link has changed is not relivant.
14056  *
14057  * Fork - LGPL
14058  * <script type="text/javascript">
14059  */
14060  
14061 // Base class for reading structured data from a data source.  This class is intended to be
14062 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14063
14064 /**
14065  * @class Roo.data.DataReader
14066  * Base class for reading structured data from a data source.  This class is intended to be
14067  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14068  */
14069
14070 Roo.data.DataReader = function(meta, recordType){
14071     
14072     this.meta = meta;
14073     
14074     this.recordType = recordType instanceof Array ? 
14075         Roo.data.Record.create(recordType) : recordType;
14076 };
14077
14078 Roo.data.DataReader.prototype = {
14079     
14080     
14081     readerType : 'Data',
14082      /**
14083      * Create an empty record
14084      * @param {Object} data (optional) - overlay some values
14085      * @return {Roo.data.Record} record created.
14086      */
14087     newRow :  function(d) {
14088         var da =  {};
14089         this.recordType.prototype.fields.each(function(c) {
14090             switch( c.type) {
14091                 case 'int' : da[c.name] = 0; break;
14092                 case 'date' : da[c.name] = new Date(); break;
14093                 case 'float' : da[c.name] = 0.0; break;
14094                 case 'boolean' : da[c.name] = false; break;
14095                 default : da[c.name] = ""; break;
14096             }
14097             
14098         });
14099         return new this.recordType(Roo.apply(da, d));
14100     }
14101     
14102     
14103 };/*
14104  * Based on:
14105  * Ext JS Library 1.1.1
14106  * Copyright(c) 2006-2007, Ext JS, LLC.
14107  *
14108  * Originally Released Under LGPL - original licence link has changed is not relivant.
14109  *
14110  * Fork - LGPL
14111  * <script type="text/javascript">
14112  */
14113
14114 /**
14115  * @class Roo.data.DataProxy
14116  * @extends Roo.data.Observable
14117  * This class is an abstract base class for implementations which provide retrieval of
14118  * unformatted data objects.<br>
14119  * <p>
14120  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14121  * (of the appropriate type which knows how to parse the data object) to provide a block of
14122  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14123  * <p>
14124  * Custom implementations must implement the load method as described in
14125  * {@link Roo.data.HttpProxy#load}.
14126  */
14127 Roo.data.DataProxy = function(){
14128     this.addEvents({
14129         /**
14130          * @event beforeload
14131          * Fires before a network request is made to retrieve a data object.
14132          * @param {Object} This DataProxy object.
14133          * @param {Object} params The params parameter to the load function.
14134          */
14135         beforeload : true,
14136         /**
14137          * @event load
14138          * Fires before the load method's callback is called.
14139          * @param {Object} This DataProxy object.
14140          * @param {Object} o The data object.
14141          * @param {Object} arg The callback argument object passed to the load function.
14142          */
14143         load : true,
14144         /**
14145          * @event loadexception
14146          * Fires if an Exception occurs during data retrieval.
14147          * @param {Object} This DataProxy object.
14148          * @param {Object} o The data object.
14149          * @param {Object} arg The callback argument object passed to the load function.
14150          * @param {Object} e The Exception.
14151          */
14152         loadexception : true
14153     });
14154     Roo.data.DataProxy.superclass.constructor.call(this);
14155 };
14156
14157 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14158
14159     /**
14160      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14161      */
14162 /*
14163  * Based on:
14164  * Ext JS Library 1.1.1
14165  * Copyright(c) 2006-2007, Ext JS, LLC.
14166  *
14167  * Originally Released Under LGPL - original licence link has changed is not relivant.
14168  *
14169  * Fork - LGPL
14170  * <script type="text/javascript">
14171  */
14172 /**
14173  * @class Roo.data.MemoryProxy
14174  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14175  * to the Reader when its load method is called.
14176  * @constructor
14177  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14178  */
14179 Roo.data.MemoryProxy = function(data){
14180     if (data.data) {
14181         data = data.data;
14182     }
14183     Roo.data.MemoryProxy.superclass.constructor.call(this);
14184     this.data = data;
14185 };
14186
14187 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14188     
14189     /**
14190      * Load data from the requested source (in this case an in-memory
14191      * data object passed to the constructor), read the data object into
14192      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14193      * process that block using the passed callback.
14194      * @param {Object} params This parameter is not used by the MemoryProxy class.
14195      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14196      * object into a block of Roo.data.Records.
14197      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14198      * The function must be passed <ul>
14199      * <li>The Record block object</li>
14200      * <li>The "arg" argument from the load function</li>
14201      * <li>A boolean success indicator</li>
14202      * </ul>
14203      * @param {Object} scope The scope in which to call the callback
14204      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14205      */
14206     load : function(params, reader, callback, scope, arg){
14207         params = params || {};
14208         var result;
14209         try {
14210             result = reader.readRecords(params.data ? params.data :this.data);
14211         }catch(e){
14212             this.fireEvent("loadexception", this, arg, null, e);
14213             callback.call(scope, null, arg, false);
14214             return;
14215         }
14216         callback.call(scope, result, arg, true);
14217     },
14218     
14219     // private
14220     update : function(params, records){
14221         
14222     }
14223 });/*
14224  * Based on:
14225  * Ext JS Library 1.1.1
14226  * Copyright(c) 2006-2007, Ext JS, LLC.
14227  *
14228  * Originally Released Under LGPL - original licence link has changed is not relivant.
14229  *
14230  * Fork - LGPL
14231  * <script type="text/javascript">
14232  */
14233 /**
14234  * @class Roo.data.HttpProxy
14235  * @extends Roo.data.DataProxy
14236  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14237  * configured to reference a certain URL.<br><br>
14238  * <p>
14239  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14240  * from which the running page was served.<br><br>
14241  * <p>
14242  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14243  * <p>
14244  * Be aware that to enable the browser to parse an XML document, the server must set
14245  * the Content-Type header in the HTTP response to "text/xml".
14246  * @constructor
14247  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14248  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14249  * will be used to make the request.
14250  */
14251 Roo.data.HttpProxy = function(conn){
14252     Roo.data.HttpProxy.superclass.constructor.call(this);
14253     // is conn a conn config or a real conn?
14254     this.conn = conn;
14255     this.useAjax = !conn || !conn.events;
14256   
14257 };
14258
14259 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14260     // thse are take from connection...
14261     
14262     /**
14263      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14264      */
14265     /**
14266      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14267      * extra parameters to each request made by this object. (defaults to undefined)
14268      */
14269     /**
14270      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14271      *  to each request made by this object. (defaults to undefined)
14272      */
14273     /**
14274      * @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)
14275      */
14276     /**
14277      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14278      */
14279      /**
14280      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14281      * @type Boolean
14282      */
14283   
14284
14285     /**
14286      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14287      * @type Boolean
14288      */
14289     /**
14290      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14291      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14292      * a finer-grained basis than the DataProxy events.
14293      */
14294     getConnection : function(){
14295         return this.useAjax ? Roo.Ajax : this.conn;
14296     },
14297
14298     /**
14299      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14300      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14301      * process that block using the passed callback.
14302      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14303      * for the request to the remote server.
14304      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14305      * object into a block of Roo.data.Records.
14306      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14307      * The function must be passed <ul>
14308      * <li>The Record block object</li>
14309      * <li>The "arg" argument from the load function</li>
14310      * <li>A boolean success indicator</li>
14311      * </ul>
14312      * @param {Object} scope The scope in which to call the callback
14313      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14314      */
14315     load : function(params, reader, callback, scope, arg){
14316         if(this.fireEvent("beforeload", this, params) !== false){
14317             var  o = {
14318                 params : params || {},
14319                 request: {
14320                     callback : callback,
14321                     scope : scope,
14322                     arg : arg
14323                 },
14324                 reader: reader,
14325                 callback : this.loadResponse,
14326                 scope: this
14327             };
14328             if(this.useAjax){
14329                 Roo.applyIf(o, this.conn);
14330                 if(this.activeRequest){
14331                     Roo.Ajax.abort(this.activeRequest);
14332                 }
14333                 this.activeRequest = Roo.Ajax.request(o);
14334             }else{
14335                 this.conn.request(o);
14336             }
14337         }else{
14338             callback.call(scope||this, null, arg, false);
14339         }
14340     },
14341
14342     // private
14343     loadResponse : function(o, success, response){
14344         delete this.activeRequest;
14345         if(!success){
14346             this.fireEvent("loadexception", this, o, response);
14347             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14348             return;
14349         }
14350         var result;
14351         try {
14352             result = o.reader.read(response);
14353         }catch(e){
14354             this.fireEvent("loadexception", this, o, response, e);
14355             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14356             return;
14357         }
14358         
14359         this.fireEvent("load", this, o, o.request.arg);
14360         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14361     },
14362
14363     // private
14364     update : function(dataSet){
14365
14366     },
14367
14368     // private
14369     updateResponse : function(dataSet){
14370
14371     }
14372 });/*
14373  * Based on:
14374  * Ext JS Library 1.1.1
14375  * Copyright(c) 2006-2007, Ext JS, LLC.
14376  *
14377  * Originally Released Under LGPL - original licence link has changed is not relivant.
14378  *
14379  * Fork - LGPL
14380  * <script type="text/javascript">
14381  */
14382
14383 /**
14384  * @class Roo.data.ScriptTagProxy
14385  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14386  * other than the originating domain of the running page.<br><br>
14387  * <p>
14388  * <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
14389  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14390  * <p>
14391  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14392  * source code that is used as the source inside a &lt;script> tag.<br><br>
14393  * <p>
14394  * In order for the browser to process the returned data, the server must wrap the data object
14395  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14396  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14397  * depending on whether the callback name was passed:
14398  * <p>
14399  * <pre><code>
14400 boolean scriptTag = false;
14401 String cb = request.getParameter("callback");
14402 if (cb != null) {
14403     scriptTag = true;
14404     response.setContentType("text/javascript");
14405 } else {
14406     response.setContentType("application/x-json");
14407 }
14408 Writer out = response.getWriter();
14409 if (scriptTag) {
14410     out.write(cb + "(");
14411 }
14412 out.print(dataBlock.toJsonString());
14413 if (scriptTag) {
14414     out.write(");");
14415 }
14416 </pre></code>
14417  *
14418  * @constructor
14419  * @param {Object} config A configuration object.
14420  */
14421 Roo.data.ScriptTagProxy = function(config){
14422     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14423     Roo.apply(this, config);
14424     this.head = document.getElementsByTagName("head")[0];
14425 };
14426
14427 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14428
14429 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14430     /**
14431      * @cfg {String} url The URL from which to request the data object.
14432      */
14433     /**
14434      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14435      */
14436     timeout : 30000,
14437     /**
14438      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14439      * the server the name of the callback function set up by the load call to process the returned data object.
14440      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14441      * javascript output which calls this named function passing the data object as its only parameter.
14442      */
14443     callbackParam : "callback",
14444     /**
14445      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14446      * name to the request.
14447      */
14448     nocache : true,
14449
14450     /**
14451      * Load data from the configured URL, read the data object into
14452      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14453      * process that block using the passed callback.
14454      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14455      * for the request to the remote server.
14456      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14457      * object into a block of Roo.data.Records.
14458      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14459      * The function must be passed <ul>
14460      * <li>The Record block object</li>
14461      * <li>The "arg" argument from the load function</li>
14462      * <li>A boolean success indicator</li>
14463      * </ul>
14464      * @param {Object} scope The scope in which to call the callback
14465      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14466      */
14467     load : function(params, reader, callback, scope, arg){
14468         if(this.fireEvent("beforeload", this, params) !== false){
14469
14470             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14471
14472             var url = this.url;
14473             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14474             if(this.nocache){
14475                 url += "&_dc=" + (new Date().getTime());
14476             }
14477             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14478             var trans = {
14479                 id : transId,
14480                 cb : "stcCallback"+transId,
14481                 scriptId : "stcScript"+transId,
14482                 params : params,
14483                 arg : arg,
14484                 url : url,
14485                 callback : callback,
14486                 scope : scope,
14487                 reader : reader
14488             };
14489             var conn = this;
14490
14491             window[trans.cb] = function(o){
14492                 conn.handleResponse(o, trans);
14493             };
14494
14495             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14496
14497             if(this.autoAbort !== false){
14498                 this.abort();
14499             }
14500
14501             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14502
14503             var script = document.createElement("script");
14504             script.setAttribute("src", url);
14505             script.setAttribute("type", "text/javascript");
14506             script.setAttribute("id", trans.scriptId);
14507             this.head.appendChild(script);
14508
14509             this.trans = trans;
14510         }else{
14511             callback.call(scope||this, null, arg, false);
14512         }
14513     },
14514
14515     // private
14516     isLoading : function(){
14517         return this.trans ? true : false;
14518     },
14519
14520     /**
14521      * Abort the current server request.
14522      */
14523     abort : function(){
14524         if(this.isLoading()){
14525             this.destroyTrans(this.trans);
14526         }
14527     },
14528
14529     // private
14530     destroyTrans : function(trans, isLoaded){
14531         this.head.removeChild(document.getElementById(trans.scriptId));
14532         clearTimeout(trans.timeoutId);
14533         if(isLoaded){
14534             window[trans.cb] = undefined;
14535             try{
14536                 delete window[trans.cb];
14537             }catch(e){}
14538         }else{
14539             // if hasn't been loaded, wait for load to remove it to prevent script error
14540             window[trans.cb] = function(){
14541                 window[trans.cb] = undefined;
14542                 try{
14543                     delete window[trans.cb];
14544                 }catch(e){}
14545             };
14546         }
14547     },
14548
14549     // private
14550     handleResponse : function(o, trans){
14551         this.trans = false;
14552         this.destroyTrans(trans, true);
14553         var result;
14554         try {
14555             result = trans.reader.readRecords(o);
14556         }catch(e){
14557             this.fireEvent("loadexception", this, o, trans.arg, e);
14558             trans.callback.call(trans.scope||window, null, trans.arg, false);
14559             return;
14560         }
14561         this.fireEvent("load", this, o, trans.arg);
14562         trans.callback.call(trans.scope||window, result, trans.arg, true);
14563     },
14564
14565     // private
14566     handleFailure : function(trans){
14567         this.trans = false;
14568         this.destroyTrans(trans, false);
14569         this.fireEvent("loadexception", this, null, trans.arg);
14570         trans.callback.call(trans.scope||window, null, trans.arg, false);
14571     }
14572 });/*
14573  * Based on:
14574  * Ext JS Library 1.1.1
14575  * Copyright(c) 2006-2007, Ext JS, LLC.
14576  *
14577  * Originally Released Under LGPL - original licence link has changed is not relivant.
14578  *
14579  * Fork - LGPL
14580  * <script type="text/javascript">
14581  */
14582
14583 /**
14584  * @class Roo.data.JsonReader
14585  * @extends Roo.data.DataReader
14586  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14587  * based on mappings in a provided Roo.data.Record constructor.
14588  * 
14589  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14590  * in the reply previously. 
14591  * 
14592  * <p>
14593  * Example code:
14594  * <pre><code>
14595 var RecordDef = Roo.data.Record.create([
14596     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14597     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14598 ]);
14599 var myReader = new Roo.data.JsonReader({
14600     totalProperty: "results",    // The property which contains the total dataset size (optional)
14601     root: "rows",                // The property which contains an Array of row objects
14602     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14603 }, RecordDef);
14604 </code></pre>
14605  * <p>
14606  * This would consume a JSON file like this:
14607  * <pre><code>
14608 { 'results': 2, 'rows': [
14609     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14610     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14611 }
14612 </code></pre>
14613  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14614  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14615  * paged from the remote server.
14616  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14617  * @cfg {String} root name of the property which contains the Array of row objects.
14618  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14619  * @cfg {Array} fields Array of field definition objects
14620  * @constructor
14621  * Create a new JsonReader
14622  * @param {Object} meta Metadata configuration options
14623  * @param {Object} recordType Either an Array of field definition objects,
14624  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14625  */
14626 Roo.data.JsonReader = function(meta, recordType){
14627     
14628     meta = meta || {};
14629     // set some defaults:
14630     Roo.applyIf(meta, {
14631         totalProperty: 'total',
14632         successProperty : 'success',
14633         root : 'data',
14634         id : 'id'
14635     });
14636     
14637     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14638 };
14639 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14640     
14641     readerType : 'Json',
14642     
14643     /**
14644      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14645      * Used by Store query builder to append _requestMeta to params.
14646      * 
14647      */
14648     metaFromRemote : false,
14649     /**
14650      * This method is only used by a DataProxy which has retrieved data from a remote server.
14651      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14652      * @return {Object} data A data block which is used by an Roo.data.Store object as
14653      * a cache of Roo.data.Records.
14654      */
14655     read : function(response){
14656         var json = response.responseText;
14657        
14658         var o = /* eval:var:o */ eval("("+json+")");
14659         if(!o) {
14660             throw {message: "JsonReader.read: Json object not found"};
14661         }
14662         
14663         if(o.metaData){
14664             
14665             delete this.ef;
14666             this.metaFromRemote = true;
14667             this.meta = o.metaData;
14668             this.recordType = Roo.data.Record.create(o.metaData.fields);
14669             this.onMetaChange(this.meta, this.recordType, o);
14670         }
14671         return this.readRecords(o);
14672     },
14673
14674     // private function a store will implement
14675     onMetaChange : function(meta, recordType, o){
14676
14677     },
14678
14679     /**
14680          * @ignore
14681          */
14682     simpleAccess: function(obj, subsc) {
14683         return obj[subsc];
14684     },
14685
14686         /**
14687          * @ignore
14688          */
14689     getJsonAccessor: function(){
14690         var re = /[\[\.]/;
14691         return function(expr) {
14692             try {
14693                 return(re.test(expr))
14694                     ? new Function("obj", "return obj." + expr)
14695                     : function(obj){
14696                         return obj[expr];
14697                     };
14698             } catch(e){}
14699             return Roo.emptyFn;
14700         };
14701     }(),
14702
14703     /**
14704      * Create a data block containing Roo.data.Records from an XML document.
14705      * @param {Object} o An object which contains an Array of row objects in the property specified
14706      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14707      * which contains the total size of the dataset.
14708      * @return {Object} data A data block which is used by an Roo.data.Store object as
14709      * a cache of Roo.data.Records.
14710      */
14711     readRecords : function(o){
14712         /**
14713          * After any data loads, the raw JSON data is available for further custom processing.
14714          * @type Object
14715          */
14716         this.o = o;
14717         var s = this.meta, Record = this.recordType,
14718             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14719
14720 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14721         if (!this.ef) {
14722             if(s.totalProperty) {
14723                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14724                 }
14725                 if(s.successProperty) {
14726                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14727                 }
14728                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14729                 if (s.id) {
14730                         var g = this.getJsonAccessor(s.id);
14731                         this.getId = function(rec) {
14732                                 var r = g(rec);  
14733                                 return (r === undefined || r === "") ? null : r;
14734                         };
14735                 } else {
14736                         this.getId = function(){return null;};
14737                 }
14738             this.ef = [];
14739             for(var jj = 0; jj < fl; jj++){
14740                 f = fi[jj];
14741                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14742                 this.ef[jj] = this.getJsonAccessor(map);
14743             }
14744         }
14745
14746         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14747         if(s.totalProperty){
14748             var vt = parseInt(this.getTotal(o), 10);
14749             if(!isNaN(vt)){
14750                 totalRecords = vt;
14751             }
14752         }
14753         if(s.successProperty){
14754             var vs = this.getSuccess(o);
14755             if(vs === false || vs === 'false'){
14756                 success = false;
14757             }
14758         }
14759         var records = [];
14760         for(var i = 0; i < c; i++){
14761                 var n = root[i];
14762             var values = {};
14763             var id = this.getId(n);
14764             for(var j = 0; j < fl; j++){
14765                 f = fi[j];
14766             var v = this.ef[j](n);
14767             if (!f.convert) {
14768                 Roo.log('missing convert for ' + f.name);
14769                 Roo.log(f);
14770                 continue;
14771             }
14772             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14773             }
14774             var record = new Record(values, id);
14775             record.json = n;
14776             records[i] = record;
14777         }
14778         return {
14779             raw : o,
14780             success : success,
14781             records : records,
14782             totalRecords : totalRecords
14783         };
14784     },
14785     // used when loading children.. @see loadDataFromChildren
14786     toLoadData: function(rec)
14787     {
14788         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14789         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14790         return { data : data, total : data.length };
14791         
14792     }
14793 });/*
14794  * Based on:
14795  * Ext JS Library 1.1.1
14796  * Copyright(c) 2006-2007, Ext JS, LLC.
14797  *
14798  * Originally Released Under LGPL - original licence link has changed is not relivant.
14799  *
14800  * Fork - LGPL
14801  * <script type="text/javascript">
14802  */
14803
14804 /**
14805  * @class Roo.data.ArrayReader
14806  * @extends Roo.data.DataReader
14807  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14808  * Each element of that Array represents a row of data fields. The
14809  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14810  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14811  * <p>
14812  * Example code:.
14813  * <pre><code>
14814 var RecordDef = Roo.data.Record.create([
14815     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14816     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14817 ]);
14818 var myReader = new Roo.data.ArrayReader({
14819     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14820 }, RecordDef);
14821 </code></pre>
14822  * <p>
14823  * This would consume an Array like this:
14824  * <pre><code>
14825 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14826   </code></pre>
14827  
14828  * @constructor
14829  * Create a new JsonReader
14830  * @param {Object} meta Metadata configuration options.
14831  * @param {Object|Array} recordType Either an Array of field definition objects
14832  * 
14833  * @cfg {Array} fields Array of field definition objects
14834  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14835  * as specified to {@link Roo.data.Record#create},
14836  * or an {@link Roo.data.Record} object
14837  *
14838  * 
14839  * created using {@link Roo.data.Record#create}.
14840  */
14841 Roo.data.ArrayReader = function(meta, recordType)
14842 {    
14843     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14844 };
14845
14846 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14847     
14848       /**
14849      * Create a data block containing Roo.data.Records from an XML document.
14850      * @param {Object} o An Array of row objects which represents the dataset.
14851      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14852      * a cache of Roo.data.Records.
14853      */
14854     readRecords : function(o)
14855     {
14856         var sid = this.meta ? this.meta.id : null;
14857         var recordType = this.recordType, fields = recordType.prototype.fields;
14858         var records = [];
14859         var root = o;
14860         for(var i = 0; i < root.length; i++){
14861                 var n = root[i];
14862             var values = {};
14863             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14864             for(var j = 0, jlen = fields.length; j < jlen; j++){
14865                 var f = fields.items[j];
14866                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14867                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14868                 v = f.convert(v);
14869                 values[f.name] = v;
14870             }
14871             var record = new recordType(values, id);
14872             record.json = n;
14873             records[records.length] = record;
14874         }
14875         return {
14876             records : records,
14877             totalRecords : records.length
14878         };
14879     },
14880     // used when loading children.. @see loadDataFromChildren
14881     toLoadData: function(rec)
14882     {
14883         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14884         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14885         
14886     }
14887     
14888     
14889 });/*
14890  * - LGPL
14891  * * 
14892  */
14893
14894 /**
14895  * @class Roo.bootstrap.ComboBox
14896  * @extends Roo.bootstrap.TriggerField
14897  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14898  * @cfg {Boolean} append (true|false) default false
14899  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14900  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14901  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14902  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14903  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14904  * @cfg {Boolean} animate default true
14905  * @cfg {Boolean} emptyResultText only for touch device
14906  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14907  * @cfg {String} emptyTitle default ''
14908  * @cfg {Number} width fixed with? experimental
14909  * @constructor
14910  * Create a new ComboBox.
14911  * @param {Object} config Configuration options
14912  */
14913 Roo.bootstrap.ComboBox = function(config){
14914     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14915     this.addEvents({
14916         /**
14917          * @event expand
14918          * Fires when the dropdown list is expanded
14919         * @param {Roo.bootstrap.ComboBox} combo This combo box
14920         */
14921         'expand' : true,
14922         /**
14923          * @event collapse
14924          * Fires when the dropdown list is collapsed
14925         * @param {Roo.bootstrap.ComboBox} combo This combo box
14926         */
14927         'collapse' : true,
14928         /**
14929          * @event beforeselect
14930          * Fires before a list item is selected. Return false to cancel the selection.
14931         * @param {Roo.bootstrap.ComboBox} combo This combo box
14932         * @param {Roo.data.Record} record The data record returned from the underlying store
14933         * @param {Number} index The index of the selected item in the dropdown list
14934         */
14935         'beforeselect' : true,
14936         /**
14937          * @event select
14938          * Fires when a list item is selected
14939         * @param {Roo.bootstrap.ComboBox} combo This combo box
14940         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14941         * @param {Number} index The index of the selected item in the dropdown list
14942         */
14943         'select' : true,
14944         /**
14945          * @event beforequery
14946          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14947          * The event object passed has these properties:
14948         * @param {Roo.bootstrap.ComboBox} combo This combo box
14949         * @param {String} query The query
14950         * @param {Boolean} forceAll true to force "all" query
14951         * @param {Boolean} cancel true to cancel the query
14952         * @param {Object} e The query event object
14953         */
14954         'beforequery': true,
14955          /**
14956          * @event add
14957          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14958         * @param {Roo.bootstrap.ComboBox} combo This combo box
14959         */
14960         'add' : true,
14961         /**
14962          * @event edit
14963          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14964         * @param {Roo.bootstrap.ComboBox} combo This combo box
14965         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14966         */
14967         'edit' : true,
14968         /**
14969          * @event remove
14970          * Fires when the remove value from the combobox array
14971         * @param {Roo.bootstrap.ComboBox} combo This combo box
14972         */
14973         'remove' : true,
14974         /**
14975          * @event afterremove
14976          * Fires when the remove value from the combobox array
14977         * @param {Roo.bootstrap.ComboBox} combo This combo box
14978         */
14979         'afterremove' : true,
14980         /**
14981          * @event specialfilter
14982          * Fires when specialfilter
14983             * @param {Roo.bootstrap.ComboBox} combo This combo box
14984             */
14985         'specialfilter' : true,
14986         /**
14987          * @event tick
14988          * Fires when tick the element
14989             * @param {Roo.bootstrap.ComboBox} combo This combo box
14990             */
14991         'tick' : true,
14992         /**
14993          * @event touchviewdisplay
14994          * Fires when touch view require special display (default is using displayField)
14995             * @param {Roo.bootstrap.ComboBox} combo This combo box
14996             * @param {Object} cfg set html .
14997             */
14998         'touchviewdisplay' : true
14999         
15000     });
15001     
15002     this.item = [];
15003     this.tickItems = [];
15004     
15005     this.selectedIndex = -1;
15006     if(this.mode == 'local'){
15007         if(config.queryDelay === undefined){
15008             this.queryDelay = 10;
15009         }
15010         if(config.minChars === undefined){
15011             this.minChars = 0;
15012         }
15013     }
15014 };
15015
15016 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15017      
15018     /**
15019      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15020      * rendering into an Roo.Editor, defaults to false)
15021      */
15022     /**
15023      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15024      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15025      */
15026     /**
15027      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15028      */
15029     /**
15030      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15031      * the dropdown list (defaults to undefined, with no header element)
15032      */
15033
15034      /**
15035      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15036      */
15037      
15038      /**
15039      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15040      */
15041     listWidth: undefined,
15042     /**
15043      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15044      * mode = 'remote' or 'text' if mode = 'local')
15045      */
15046     displayField: undefined,
15047     
15048     /**
15049      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15050      * mode = 'remote' or 'value' if mode = 'local'). 
15051      * Note: use of a valueField requires the user make a selection
15052      * in order for a value to be mapped.
15053      */
15054     valueField: undefined,
15055     /**
15056      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15057      */
15058     modalTitle : '',
15059     
15060     /**
15061      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15062      * field's data value (defaults to the underlying DOM element's name)
15063      */
15064     hiddenName: undefined,
15065     /**
15066      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15067      */
15068     listClass: '',
15069     /**
15070      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15071      */
15072     selectedClass: 'active',
15073     
15074     /**
15075      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15076      */
15077     shadow:'sides',
15078     /**
15079      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15080      * anchor positions (defaults to 'tl-bl')
15081      */
15082     listAlign: 'tl-bl?',
15083     /**
15084      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15085      */
15086     maxHeight: 300,
15087     /**
15088      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15089      * query specified by the allQuery config option (defaults to 'query')
15090      */
15091     triggerAction: 'query',
15092     /**
15093      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15094      * (defaults to 4, does not apply if editable = false)
15095      */
15096     minChars : 4,
15097     /**
15098      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15099      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15100      */
15101     typeAhead: false,
15102     /**
15103      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15104      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15105      */
15106     queryDelay: 500,
15107     /**
15108      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15109      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15110      */
15111     pageSize: 0,
15112     /**
15113      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15114      * when editable = true (defaults to false)
15115      */
15116     selectOnFocus:false,
15117     /**
15118      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15119      */
15120     queryParam: 'query',
15121     /**
15122      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15123      * when mode = 'remote' (defaults to 'Loading...')
15124      */
15125     loadingText: 'Loading...',
15126     /**
15127      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15128      */
15129     resizable: false,
15130     /**
15131      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15132      */
15133     handleHeight : 8,
15134     /**
15135      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15136      * traditional select (defaults to true)
15137      */
15138     editable: true,
15139     /**
15140      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15141      */
15142     allQuery: '',
15143     /**
15144      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15145      */
15146     mode: 'remote',
15147     /**
15148      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15149      * listWidth has a higher value)
15150      */
15151     minListWidth : 70,
15152     /**
15153      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15154      * allow the user to set arbitrary text into the field (defaults to false)
15155      */
15156     forceSelection:false,
15157     /**
15158      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15159      * if typeAhead = true (defaults to 250)
15160      */
15161     typeAheadDelay : 250,
15162     /**
15163      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15164      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15165      */
15166     valueNotFoundText : undefined,
15167     /**
15168      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15169      */
15170     blockFocus : false,
15171     
15172     /**
15173      * @cfg {Boolean} disableClear Disable showing of clear button.
15174      */
15175     disableClear : false,
15176     /**
15177      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15178      */
15179     alwaysQuery : false,
15180     
15181     /**
15182      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15183      */
15184     multiple : false,
15185     
15186     /**
15187      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15188      */
15189     invalidClass : "has-warning",
15190     
15191     /**
15192      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15193      */
15194     validClass : "has-success",
15195     
15196     /**
15197      * @cfg {Boolean} specialFilter (true|false) special filter default false
15198      */
15199     specialFilter : false,
15200     
15201     /**
15202      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15203      */
15204     mobileTouchView : true,
15205     
15206     /**
15207      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15208      */
15209     useNativeIOS : false,
15210     
15211     /**
15212      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15213      */
15214     mobile_restrict_height : false,
15215     
15216     ios_options : false,
15217     
15218     //private
15219     addicon : false,
15220     editicon: false,
15221     
15222     page: 0,
15223     hasQuery: false,
15224     append: false,
15225     loadNext: false,
15226     autoFocus : true,
15227     tickable : false,
15228     btnPosition : 'right',
15229     triggerList : true,
15230     showToggleBtn : true,
15231     animate : true,
15232     emptyResultText: 'Empty',
15233     triggerText : 'Select',
15234     emptyTitle : '',
15235     width : false,
15236     
15237     // element that contains real text value.. (when hidden is used..)
15238     
15239     getAutoCreate : function()
15240     {   
15241         var cfg = false;
15242         //render
15243         /*
15244          * Render classic select for iso
15245          */
15246         
15247         if(Roo.isIOS && this.useNativeIOS){
15248             cfg = this.getAutoCreateNativeIOS();
15249             return cfg;
15250         }
15251         
15252         /*
15253          * Touch Devices
15254          */
15255         
15256         if(Roo.isTouch && this.mobileTouchView){
15257             cfg = this.getAutoCreateTouchView();
15258             return cfg;;
15259         }
15260         
15261         /*
15262          *  Normal ComboBox
15263          */
15264         if(!this.tickable){
15265             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15266             return cfg;
15267         }
15268         
15269         /*
15270          *  ComboBox with tickable selections
15271          */
15272              
15273         var align = this.labelAlign || this.parentLabelAlign();
15274         
15275         cfg = {
15276             cls : 'form-group roo-combobox-tickable' //input-group
15277         };
15278         
15279         var btn_text_select = '';
15280         var btn_text_done = '';
15281         var btn_text_cancel = '';
15282         
15283         if (this.btn_text_show) {
15284             btn_text_select = 'Select';
15285             btn_text_done = 'Done';
15286             btn_text_cancel = 'Cancel'; 
15287         }
15288         
15289         var buttons = {
15290             tag : 'div',
15291             cls : 'tickable-buttons',
15292             cn : [
15293                 {
15294                     tag : 'button',
15295                     type : 'button',
15296                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15297                     //html : this.triggerText
15298                     html: btn_text_select
15299                 },
15300                 {
15301                     tag : 'button',
15302                     type : 'button',
15303                     name : 'ok',
15304                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15305                     //html : 'Done'
15306                     html: btn_text_done
15307                 },
15308                 {
15309                     tag : 'button',
15310                     type : 'button',
15311                     name : 'cancel',
15312                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15313                     //html : 'Cancel'
15314                     html: btn_text_cancel
15315                 }
15316             ]
15317         };
15318         
15319         if(this.editable){
15320             buttons.cn.unshift({
15321                 tag: 'input',
15322                 cls: 'roo-select2-search-field-input'
15323             });
15324         }
15325         
15326         var _this = this;
15327         
15328         Roo.each(buttons.cn, function(c){
15329             if (_this.size) {
15330                 c.cls += ' btn-' + _this.size;
15331             }
15332
15333             if (_this.disabled) {
15334                 c.disabled = true;
15335             }
15336         });
15337         
15338         var box = {
15339             tag: 'div',
15340             style : 'display: contents',
15341             cn: [
15342                 {
15343                     tag: 'input',
15344                     type : 'hidden',
15345                     cls: 'form-hidden-field'
15346                 },
15347                 {
15348                     tag: 'ul',
15349                     cls: 'roo-select2-choices',
15350                     cn:[
15351                         {
15352                             tag: 'li',
15353                             cls: 'roo-select2-search-field',
15354                             cn: [
15355                                 buttons
15356                             ]
15357                         }
15358                     ]
15359                 }
15360             ]
15361         };
15362         
15363         var combobox = {
15364             cls: 'roo-select2-container input-group roo-select2-container-multi',
15365             cn: [
15366                 
15367                 box
15368 //                {
15369 //                    tag: 'ul',
15370 //                    cls: 'typeahead typeahead-long dropdown-menu',
15371 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15372 //                }
15373             ]
15374         };
15375         
15376         if(this.hasFeedback && !this.allowBlank){
15377             
15378             var feedback = {
15379                 tag: 'span',
15380                 cls: 'glyphicon form-control-feedback'
15381             };
15382
15383             combobox.cn.push(feedback);
15384         }
15385         
15386         
15387         
15388         var indicator = {
15389             tag : 'i',
15390             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15391             tooltip : 'This field is required'
15392         };
15393         if (Roo.bootstrap.version == 4) {
15394             indicator = {
15395                 tag : 'i',
15396                 style : 'display:none'
15397             };
15398         }
15399         if (align ==='left' && this.fieldLabel.length) {
15400             
15401             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15402             
15403             cfg.cn = [
15404                 indicator,
15405                 {
15406                     tag: 'label',
15407                     'for' :  id,
15408                     cls : 'control-label col-form-label',
15409                     html : this.fieldLabel
15410
15411                 },
15412                 {
15413                     cls : "", 
15414                     cn: [
15415                         combobox
15416                     ]
15417                 }
15418
15419             ];
15420             
15421             var labelCfg = cfg.cn[1];
15422             var contentCfg = cfg.cn[2];
15423             
15424
15425             if(this.indicatorpos == 'right'){
15426                 
15427                 cfg.cn = [
15428                     {
15429                         tag: 'label',
15430                         'for' :  id,
15431                         cls : 'control-label col-form-label',
15432                         cn : [
15433                             {
15434                                 tag : 'span',
15435                                 html : this.fieldLabel
15436                             },
15437                             indicator
15438                         ]
15439                     },
15440                     {
15441                         cls : "",
15442                         cn: [
15443                             combobox
15444                         ]
15445                     }
15446
15447                 ];
15448                 
15449                 
15450                 
15451                 labelCfg = cfg.cn[0];
15452                 contentCfg = cfg.cn[1];
15453             
15454             }
15455             
15456             if(this.labelWidth > 12){
15457                 labelCfg.style = "width: " + this.labelWidth + 'px';
15458             }
15459             if(this.width * 1 > 0){
15460                 contentCfg.style = "width: " + this.width + 'px';
15461             }
15462             if(this.labelWidth < 13 && this.labelmd == 0){
15463                 this.labelmd = this.labelWidth;
15464             }
15465             
15466             if(this.labellg > 0){
15467                 labelCfg.cls += ' col-lg-' + this.labellg;
15468                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15469             }
15470             
15471             if(this.labelmd > 0){
15472                 labelCfg.cls += ' col-md-' + this.labelmd;
15473                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15474             }
15475             
15476             if(this.labelsm > 0){
15477                 labelCfg.cls += ' col-sm-' + this.labelsm;
15478                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15479             }
15480             
15481             if(this.labelxs > 0){
15482                 labelCfg.cls += ' col-xs-' + this.labelxs;
15483                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15484             }
15485                 
15486                 
15487         } else if ( this.fieldLabel.length) {
15488 //                Roo.log(" label");
15489                  cfg.cn = [
15490                    indicator,
15491                     {
15492                         tag: 'label',
15493                         //cls : 'input-group-addon',
15494                         html : this.fieldLabel
15495                     },
15496                     combobox
15497                 ];
15498                 
15499                 if(this.indicatorpos == 'right'){
15500                     cfg.cn = [
15501                         {
15502                             tag: 'label',
15503                             //cls : 'input-group-addon',
15504                             html : this.fieldLabel
15505                         },
15506                         indicator,
15507                         combobox
15508                     ];
15509                     
15510                 }
15511
15512         } else {
15513             
15514 //                Roo.log(" no label && no align");
15515                 cfg = combobox
15516                      
15517                 
15518         }
15519          
15520         var settings=this;
15521         ['xs','sm','md','lg'].map(function(size){
15522             if (settings[size]) {
15523                 cfg.cls += ' col-' + size + '-' + settings[size];
15524             }
15525         });
15526         
15527         return cfg;
15528         
15529     },
15530     
15531     _initEventsCalled : false,
15532     
15533     // private
15534     initEvents: function()
15535     {   
15536         if (this._initEventsCalled) { // as we call render... prevent looping...
15537             return;
15538         }
15539         this._initEventsCalled = true;
15540         
15541         if (!this.store) {
15542             throw "can not find store for combo";
15543         }
15544         
15545         this.indicator = this.indicatorEl();
15546         
15547         this.store = Roo.factory(this.store, Roo.data);
15548         this.store.parent = this;
15549         
15550         // if we are building from html. then this element is so complex, that we can not really
15551         // use the rendered HTML.
15552         // so we have to trash and replace the previous code.
15553         if (Roo.XComponent.build_from_html) {
15554             // remove this element....
15555             var e = this.el.dom, k=0;
15556             while (e ) { e = e.previousSibling;  ++k;}
15557
15558             this.el.remove();
15559             
15560             this.el=false;
15561             this.rendered = false;
15562             
15563             this.render(this.parent().getChildContainer(true), k);
15564         }
15565         
15566         if(Roo.isIOS && this.useNativeIOS){
15567             this.initIOSView();
15568             return;
15569         }
15570         
15571         /*
15572          * Touch Devices
15573          */
15574         
15575         if(Roo.isTouch && this.mobileTouchView){
15576             this.initTouchView();
15577             return;
15578         }
15579         
15580         if(this.tickable){
15581             this.initTickableEvents();
15582             return;
15583         }
15584         
15585         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15586         
15587         if(this.hiddenName){
15588             
15589             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15590             
15591             this.hiddenField.dom.value =
15592                 this.hiddenValue !== undefined ? this.hiddenValue :
15593                 this.value !== undefined ? this.value : '';
15594
15595             // prevent input submission
15596             this.el.dom.removeAttribute('name');
15597             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15598              
15599              
15600         }
15601         //if(Roo.isGecko){
15602         //    this.el.dom.setAttribute('autocomplete', 'off');
15603         //}
15604         
15605         var cls = 'x-combo-list';
15606         
15607         //this.list = new Roo.Layer({
15608         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15609         //});
15610         
15611         var _this = this;
15612         
15613         (function(){
15614             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15615             _this.list.setWidth(lw);
15616         }).defer(100);
15617         
15618         this.list.on('mouseover', this.onViewOver, this);
15619         this.list.on('mousemove', this.onViewMove, this);
15620         this.list.on('scroll', this.onViewScroll, this);
15621         
15622         /*
15623         this.list.swallowEvent('mousewheel');
15624         this.assetHeight = 0;
15625
15626         if(this.title){
15627             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15628             this.assetHeight += this.header.getHeight();
15629         }
15630
15631         this.innerList = this.list.createChild({cls:cls+'-inner'});
15632         this.innerList.on('mouseover', this.onViewOver, this);
15633         this.innerList.on('mousemove', this.onViewMove, this);
15634         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15635         
15636         if(this.allowBlank && !this.pageSize && !this.disableClear){
15637             this.footer = this.list.createChild({cls:cls+'-ft'});
15638             this.pageTb = new Roo.Toolbar(this.footer);
15639            
15640         }
15641         if(this.pageSize){
15642             this.footer = this.list.createChild({cls:cls+'-ft'});
15643             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15644                     {pageSize: this.pageSize});
15645             
15646         }
15647         
15648         if (this.pageTb && this.allowBlank && !this.disableClear) {
15649             var _this = this;
15650             this.pageTb.add(new Roo.Toolbar.Fill(), {
15651                 cls: 'x-btn-icon x-btn-clear',
15652                 text: '&#160;',
15653                 handler: function()
15654                 {
15655                     _this.collapse();
15656                     _this.clearValue();
15657                     _this.onSelect(false, -1);
15658                 }
15659             });
15660         }
15661         if (this.footer) {
15662             this.assetHeight += this.footer.getHeight();
15663         }
15664         */
15665             
15666         if(!this.tpl){
15667             this.tpl = Roo.bootstrap.version == 4 ?
15668                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15669                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15670         }
15671
15672         this.view = new Roo.View(this.list, this.tpl, {
15673             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15674         });
15675         //this.view.wrapEl.setDisplayed(false);
15676         this.view.on('click', this.onViewClick, this);
15677         
15678         
15679         this.store.on('beforeload', this.onBeforeLoad, this);
15680         this.store.on('load', this.onLoad, this);
15681         this.store.on('loadexception', this.onLoadException, this);
15682         /*
15683         if(this.resizable){
15684             this.resizer = new Roo.Resizable(this.list,  {
15685                pinned:true, handles:'se'
15686             });
15687             this.resizer.on('resize', function(r, w, h){
15688                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15689                 this.listWidth = w;
15690                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15691                 this.restrictHeight();
15692             }, this);
15693             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15694         }
15695         */
15696         if(!this.editable){
15697             this.editable = true;
15698             this.setEditable(false);
15699         }
15700         
15701         /*
15702         
15703         if (typeof(this.events.add.listeners) != 'undefined') {
15704             
15705             this.addicon = this.wrap.createChild(
15706                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15707        
15708             this.addicon.on('click', function(e) {
15709                 this.fireEvent('add', this);
15710             }, this);
15711         }
15712         if (typeof(this.events.edit.listeners) != 'undefined') {
15713             
15714             this.editicon = this.wrap.createChild(
15715                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15716             if (this.addicon) {
15717                 this.editicon.setStyle('margin-left', '40px');
15718             }
15719             this.editicon.on('click', function(e) {
15720                 
15721                 // we fire even  if inothing is selected..
15722                 this.fireEvent('edit', this, this.lastData );
15723                 
15724             }, this);
15725         }
15726         */
15727         
15728         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15729             "up" : function(e){
15730                 this.inKeyMode = true;
15731                 this.selectPrev();
15732             },
15733
15734             "down" : function(e){
15735                 if(!this.isExpanded()){
15736                     this.onTriggerClick();
15737                 }else{
15738                     this.inKeyMode = true;
15739                     this.selectNext();
15740                 }
15741             },
15742
15743             "enter" : function(e){
15744 //                this.onViewClick();
15745                 //return true;
15746                 this.collapse();
15747                 
15748                 if(this.fireEvent("specialkey", this, e)){
15749                     this.onViewClick(false);
15750                 }
15751                 
15752                 return true;
15753             },
15754
15755             "esc" : function(e){
15756                 this.collapse();
15757             },
15758
15759             "tab" : function(e){
15760                 this.collapse();
15761                 
15762                 if(this.fireEvent("specialkey", this, e)){
15763                     this.onViewClick(false);
15764                 }
15765                 
15766                 return true;
15767             },
15768
15769             scope : this,
15770
15771             doRelay : function(foo, bar, hname){
15772                 if(hname == 'down' || this.scope.isExpanded()){
15773                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15774                 }
15775                 return true;
15776             },
15777
15778             forceKeyDown: true
15779         });
15780         
15781         
15782         this.queryDelay = Math.max(this.queryDelay || 10,
15783                 this.mode == 'local' ? 10 : 250);
15784         
15785         
15786         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15787         
15788         if(this.typeAhead){
15789             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15790         }
15791         if(this.editable !== false){
15792             this.inputEl().on("keyup", this.onKeyUp, this);
15793         }
15794         if(this.forceSelection){
15795             this.inputEl().on('blur', this.doForce, this);
15796         }
15797         
15798         if(this.multiple){
15799             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15800             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15801         }
15802     },
15803     
15804     initTickableEvents: function()
15805     {   
15806         this.createList();
15807         
15808         if(this.hiddenName){
15809             
15810             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15811             
15812             this.hiddenField.dom.value =
15813                 this.hiddenValue !== undefined ? this.hiddenValue :
15814                 this.value !== undefined ? this.value : '';
15815
15816             // prevent input submission
15817             this.el.dom.removeAttribute('name');
15818             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15819              
15820              
15821         }
15822         
15823 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15824         
15825         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15826         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15827         if(this.triggerList){
15828             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15829         }
15830          
15831         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15832         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15833         
15834         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15835         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15836         
15837         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15838         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15839         
15840         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15841         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15842         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15843         
15844         this.okBtn.hide();
15845         this.cancelBtn.hide();
15846         
15847         var _this = this;
15848         
15849         (function(){
15850             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15851             _this.list.setWidth(lw);
15852         }).defer(100);
15853         
15854         this.list.on('mouseover', this.onViewOver, this);
15855         this.list.on('mousemove', this.onViewMove, this);
15856         
15857         this.list.on('scroll', this.onViewScroll, this);
15858         
15859         if(!this.tpl){
15860             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15861                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15862         }
15863
15864         this.view = new Roo.View(this.list, this.tpl, {
15865             singleSelect:true,
15866             tickable:true,
15867             parent:this,
15868             store: this.store,
15869             selectedClass: this.selectedClass
15870         });
15871         
15872         //this.view.wrapEl.setDisplayed(false);
15873         this.view.on('click', this.onViewClick, this);
15874         
15875         
15876         
15877         this.store.on('beforeload', this.onBeforeLoad, this);
15878         this.store.on('load', this.onLoad, this);
15879         this.store.on('loadexception', this.onLoadException, this);
15880         
15881         if(this.editable){
15882             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15883                 "up" : function(e){
15884                     this.inKeyMode = true;
15885                     this.selectPrev();
15886                 },
15887
15888                 "down" : function(e){
15889                     this.inKeyMode = true;
15890                     this.selectNext();
15891                 },
15892
15893                 "enter" : function(e){
15894                     if(this.fireEvent("specialkey", this, e)){
15895                         this.onViewClick(false);
15896                     }
15897                     
15898                     return true;
15899                 },
15900
15901                 "esc" : function(e){
15902                     this.onTickableFooterButtonClick(e, false, false);
15903                 },
15904
15905                 "tab" : function(e){
15906                     this.fireEvent("specialkey", this, e);
15907                     
15908                     this.onTickableFooterButtonClick(e, false, false);
15909                     
15910                     return true;
15911                 },
15912
15913                 scope : this,
15914
15915                 doRelay : function(e, fn, key){
15916                     if(this.scope.isExpanded()){
15917                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15918                     }
15919                     return true;
15920                 },
15921
15922                 forceKeyDown: true
15923             });
15924         }
15925         
15926         this.queryDelay = Math.max(this.queryDelay || 10,
15927                 this.mode == 'local' ? 10 : 250);
15928         
15929         
15930         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15931         
15932         if(this.typeAhead){
15933             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15934         }
15935         
15936         if(this.editable !== false){
15937             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15938         }
15939         
15940         this.indicator = this.indicatorEl();
15941         
15942         if(this.indicator){
15943             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15944             this.indicator.hide();
15945         }
15946         
15947     },
15948
15949     onDestroy : function(){
15950         if(this.view){
15951             this.view.setStore(null);
15952             this.view.el.removeAllListeners();
15953             this.view.el.remove();
15954             this.view.purgeListeners();
15955         }
15956         if(this.list){
15957             this.list.dom.innerHTML  = '';
15958         }
15959         
15960         if(this.store){
15961             this.store.un('beforeload', this.onBeforeLoad, this);
15962             this.store.un('load', this.onLoad, this);
15963             this.store.un('loadexception', this.onLoadException, this);
15964         }
15965         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15966     },
15967
15968     // private
15969     fireKey : function(e){
15970         if(e.isNavKeyPress() && !this.list.isVisible()){
15971             this.fireEvent("specialkey", this, e);
15972         }
15973     },
15974
15975     // private
15976     onResize: function(w, h)
15977     {
15978         
15979         
15980 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15981 //        
15982 //        if(typeof w != 'number'){
15983 //            // we do not handle it!?!?
15984 //            return;
15985 //        }
15986 //        var tw = this.trigger.getWidth();
15987 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15988 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15989 //        var x = w - tw;
15990 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15991 //            
15992 //        //this.trigger.setStyle('left', x+'px');
15993 //        
15994 //        if(this.list && this.listWidth === undefined){
15995 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15996 //            this.list.setWidth(lw);
15997 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15998 //        }
15999         
16000     
16001         
16002     },
16003
16004     /**
16005      * Allow or prevent the user from directly editing the field text.  If false is passed,
16006      * the user will only be able to select from the items defined in the dropdown list.  This method
16007      * is the runtime equivalent of setting the 'editable' config option at config time.
16008      * @param {Boolean} value True to allow the user to directly edit the field text
16009      */
16010     setEditable : function(value){
16011         if(value == this.editable){
16012             return;
16013         }
16014         this.editable = value;
16015         if(!value){
16016             this.inputEl().dom.setAttribute('readOnly', true);
16017             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16018             this.inputEl().addClass('x-combo-noedit');
16019         }else{
16020             this.inputEl().dom.setAttribute('readOnly', false);
16021             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16022             this.inputEl().removeClass('x-combo-noedit');
16023         }
16024     },
16025
16026     // private
16027     
16028     onBeforeLoad : function(combo,opts){
16029         if(!this.hasFocus){
16030             return;
16031         }
16032          if (!opts.add) {
16033             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16034          }
16035         this.restrictHeight();
16036         this.selectedIndex = -1;
16037     },
16038
16039     // private
16040     onLoad : function(){
16041         
16042         this.hasQuery = false;
16043         
16044         if(!this.hasFocus){
16045             return;
16046         }
16047         
16048         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16049             this.loading.hide();
16050         }
16051         
16052         if(this.store.getCount() > 0){
16053             
16054             this.expand();
16055             this.restrictHeight();
16056             if(this.lastQuery == this.allQuery){
16057                 if(this.editable && !this.tickable){
16058                     this.inputEl().dom.select();
16059                 }
16060                 
16061                 if(
16062                     !this.selectByValue(this.value, true) &&
16063                     this.autoFocus && 
16064                     (
16065                         !this.store.lastOptions ||
16066                         typeof(this.store.lastOptions.add) == 'undefined' || 
16067                         this.store.lastOptions.add != true
16068                     )
16069                 ){
16070                     this.select(0, true);
16071                 }
16072             }else{
16073                 if(this.autoFocus){
16074                     this.selectNext();
16075                 }
16076                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16077                     this.taTask.delay(this.typeAheadDelay);
16078                 }
16079             }
16080         }else{
16081             this.onEmptyResults();
16082         }
16083         
16084         //this.el.focus();
16085     },
16086     // private
16087     onLoadException : function()
16088     {
16089         this.hasQuery = false;
16090         
16091         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16092             this.loading.hide();
16093         }
16094         
16095         if(this.tickable && this.editable){
16096             return;
16097         }
16098         
16099         this.collapse();
16100         // only causes errors at present
16101         //Roo.log(this.store.reader.jsonData);
16102         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16103             // fixme
16104             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16105         //}
16106         
16107         
16108     },
16109     // private
16110     onTypeAhead : function(){
16111         if(this.store.getCount() > 0){
16112             var r = this.store.getAt(0);
16113             var newValue = r.data[this.displayField];
16114             var len = newValue.length;
16115             var selStart = this.getRawValue().length;
16116             
16117             if(selStart != len){
16118                 this.setRawValue(newValue);
16119                 this.selectText(selStart, newValue.length);
16120             }
16121         }
16122     },
16123
16124     // private
16125     onSelect : function(record, index){
16126         
16127         if(this.fireEvent('beforeselect', this, record, index) !== false){
16128         
16129             this.setFromData(index > -1 ? record.data : false);
16130             
16131             this.collapse();
16132             this.fireEvent('select', this, record, index);
16133         }
16134     },
16135
16136     /**
16137      * Returns the currently selected field value or empty string if no value is set.
16138      * @return {String} value The selected value
16139      */
16140     getValue : function()
16141     {
16142         if(Roo.isIOS && this.useNativeIOS){
16143             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16144         }
16145         
16146         if(this.multiple){
16147             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16148         }
16149         
16150         if(this.valueField){
16151             return typeof this.value != 'undefined' ? this.value : '';
16152         }else{
16153             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16154         }
16155     },
16156     
16157     getRawValue : function()
16158     {
16159         if(Roo.isIOS && this.useNativeIOS){
16160             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16161         }
16162         
16163         var v = this.inputEl().getValue();
16164         
16165         return v;
16166     },
16167
16168     /**
16169      * Clears any text/value currently set in the field
16170      */
16171     clearValue : function(){
16172         
16173         if(this.hiddenField){
16174             this.hiddenField.dom.value = '';
16175         }
16176         this.value = '';
16177         this.setRawValue('');
16178         this.lastSelectionText = '';
16179         this.lastData = false;
16180         
16181         var close = this.closeTriggerEl();
16182         
16183         if(close){
16184             close.hide();
16185         }
16186         
16187         this.validate();
16188         
16189     },
16190
16191     /**
16192      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16193      * will be displayed in the field.  If the value does not match the data value of an existing item,
16194      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16195      * Otherwise the field will be blank (although the value will still be set).
16196      * @param {String} value The value to match
16197      */
16198     setValue : function(v)
16199     {
16200         if(Roo.isIOS && this.useNativeIOS){
16201             this.setIOSValue(v);
16202             return;
16203         }
16204         
16205         if(this.multiple){
16206             this.syncValue();
16207             return;
16208         }
16209         
16210         var text = v;
16211         if(this.valueField){
16212             var r = this.findRecord(this.valueField, v);
16213             if(r){
16214                 text = r.data[this.displayField];
16215             }else if(this.valueNotFoundText !== undefined){
16216                 text = this.valueNotFoundText;
16217             }
16218         }
16219         this.lastSelectionText = text;
16220         if(this.hiddenField){
16221             this.hiddenField.dom.value = v;
16222         }
16223         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16224         this.value = v;
16225         
16226         var close = this.closeTriggerEl();
16227         
16228         if(close){
16229             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16230         }
16231         
16232         this.validate();
16233     },
16234     /**
16235      * @property {Object} the last set data for the element
16236      */
16237     
16238     lastData : false,
16239     /**
16240      * Sets the value of the field based on a object which is related to the record format for the store.
16241      * @param {Object} value the value to set as. or false on reset?
16242      */
16243     setFromData : function(o){
16244         
16245         if(this.multiple){
16246             this.addItem(o);
16247             return;
16248         }
16249             
16250         var dv = ''; // display value
16251         var vv = ''; // value value..
16252         this.lastData = o;
16253         if (this.displayField) {
16254             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16255         } else {
16256             // this is an error condition!!!
16257             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16258         }
16259         
16260         if(this.valueField){
16261             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16262         }
16263         
16264         var close = this.closeTriggerEl();
16265         
16266         if(close){
16267             if(dv.length || vv * 1 > 0){
16268                 close.show() ;
16269                 this.blockFocus=true;
16270             } else {
16271                 close.hide();
16272             }             
16273         }
16274         
16275         if(this.hiddenField){
16276             this.hiddenField.dom.value = vv;
16277             
16278             this.lastSelectionText = dv;
16279             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16280             this.value = vv;
16281             return;
16282         }
16283         // no hidden field.. - we store the value in 'value', but still display
16284         // display field!!!!
16285         this.lastSelectionText = dv;
16286         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16287         this.value = vv;
16288         
16289         
16290         
16291     },
16292     // private
16293     reset : function(){
16294         // overridden so that last data is reset..
16295         
16296         if(this.multiple){
16297             this.clearItem();
16298             return;
16299         }
16300         
16301         this.setValue(this.originalValue);
16302         //this.clearInvalid();
16303         this.lastData = false;
16304         if (this.view) {
16305             this.view.clearSelections();
16306         }
16307         
16308         this.validate();
16309     },
16310     // private
16311     findRecord : function(prop, value){
16312         var record;
16313         if(this.store.getCount() > 0){
16314             this.store.each(function(r){
16315                 if(r.data[prop] == value){
16316                     record = r;
16317                     return false;
16318                 }
16319                 return true;
16320             });
16321         }
16322         return record;
16323     },
16324     
16325     getName: function()
16326     {
16327         // returns hidden if it's set..
16328         if (!this.rendered) {return ''};
16329         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16330         
16331     },
16332     // private
16333     onViewMove : function(e, t){
16334         this.inKeyMode = false;
16335     },
16336
16337     // private
16338     onViewOver : function(e, t){
16339         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16340             return;
16341         }
16342         var item = this.view.findItemFromChild(t);
16343         
16344         if(item){
16345             var index = this.view.indexOf(item);
16346             this.select(index, false);
16347         }
16348     },
16349
16350     // private
16351     onViewClick : function(view, doFocus, el, e)
16352     {
16353         var index = this.view.getSelectedIndexes()[0];
16354         
16355         var r = this.store.getAt(index);
16356         
16357         if(this.tickable){
16358             
16359             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16360                 return;
16361             }
16362             
16363             var rm = false;
16364             var _this = this;
16365             
16366             Roo.each(this.tickItems, function(v,k){
16367                 
16368                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16369                     Roo.log(v);
16370                     _this.tickItems.splice(k, 1);
16371                     
16372                     if(typeof(e) == 'undefined' && view == false){
16373                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16374                     }
16375                     
16376                     rm = true;
16377                     return;
16378                 }
16379             });
16380             
16381             if(rm){
16382                 return;
16383             }
16384             
16385             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16386                 this.tickItems.push(r.data);
16387             }
16388             
16389             if(typeof(e) == 'undefined' && view == false){
16390                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16391             }
16392                     
16393             return;
16394         }
16395         
16396         if(r){
16397             this.onSelect(r, index);
16398         }
16399         if(doFocus !== false && !this.blockFocus){
16400             this.inputEl().focus();
16401         }
16402     },
16403
16404     // private
16405     restrictHeight : function(){
16406         //this.innerList.dom.style.height = '';
16407         //var inner = this.innerList.dom;
16408         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16409         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16410         //this.list.beginUpdate();
16411         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16412         this.list.alignTo(this.inputEl(), this.listAlign);
16413         this.list.alignTo(this.inputEl(), this.listAlign);
16414         //this.list.endUpdate();
16415     },
16416
16417     // private
16418     onEmptyResults : function(){
16419         
16420         if(this.tickable && this.editable){
16421             this.hasFocus = false;
16422             this.restrictHeight();
16423             return;
16424         }
16425         
16426         this.collapse();
16427     },
16428
16429     /**
16430      * Returns true if the dropdown list is expanded, else false.
16431      */
16432     isExpanded : function(){
16433         return this.list.isVisible();
16434     },
16435
16436     /**
16437      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16438      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16439      * @param {String} value The data value of the item to select
16440      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16441      * selected item if it is not currently in view (defaults to true)
16442      * @return {Boolean} True if the value matched an item in the list, else false
16443      */
16444     selectByValue : function(v, scrollIntoView){
16445         if(v !== undefined && v !== null){
16446             var r = this.findRecord(this.valueField || this.displayField, v);
16447             if(r){
16448                 this.select(this.store.indexOf(r), scrollIntoView);
16449                 return true;
16450             }
16451         }
16452         return false;
16453     },
16454
16455     /**
16456      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16457      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16458      * @param {Number} index The zero-based index of the list item to select
16459      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16460      * selected item if it is not currently in view (defaults to true)
16461      */
16462     select : function(index, scrollIntoView){
16463         this.selectedIndex = index;
16464         this.view.select(index);
16465         if(scrollIntoView !== false){
16466             var el = this.view.getNode(index);
16467             /*
16468              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16469              */
16470             if(el){
16471                 this.list.scrollChildIntoView(el, false);
16472             }
16473         }
16474     },
16475
16476     // private
16477     selectNext : function(){
16478         var ct = this.store.getCount();
16479         if(ct > 0){
16480             if(this.selectedIndex == -1){
16481                 this.select(0);
16482             }else if(this.selectedIndex < ct-1){
16483                 this.select(this.selectedIndex+1);
16484             }
16485         }
16486     },
16487
16488     // private
16489     selectPrev : function(){
16490         var ct = this.store.getCount();
16491         if(ct > 0){
16492             if(this.selectedIndex == -1){
16493                 this.select(0);
16494             }else if(this.selectedIndex != 0){
16495                 this.select(this.selectedIndex-1);
16496             }
16497         }
16498     },
16499
16500     // private
16501     onKeyUp : function(e){
16502         if(this.editable !== false && !e.isSpecialKey()){
16503             this.lastKey = e.getKey();
16504             this.dqTask.delay(this.queryDelay);
16505         }
16506     },
16507
16508     // private
16509     validateBlur : function(){
16510         return !this.list || !this.list.isVisible();   
16511     },
16512
16513     // private
16514     initQuery : function(){
16515         
16516         var v = this.getRawValue();
16517         
16518         if(this.tickable && this.editable){
16519             v = this.tickableInputEl().getValue();
16520         }
16521         
16522         this.doQuery(v);
16523     },
16524
16525     // private
16526     doForce : function(){
16527         if(this.inputEl().dom.value.length > 0){
16528             this.inputEl().dom.value =
16529                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16530              
16531         }
16532     },
16533
16534     /**
16535      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16536      * query allowing the query action to be canceled if needed.
16537      * @param {String} query The SQL query to execute
16538      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16539      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16540      * saved in the current store (defaults to false)
16541      */
16542     doQuery : function(q, forceAll){
16543         
16544         if(q === undefined || q === null){
16545             q = '';
16546         }
16547         var qe = {
16548             query: q,
16549             forceAll: forceAll,
16550             combo: this,
16551             cancel:false
16552         };
16553         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16554             return false;
16555         }
16556         q = qe.query;
16557         
16558         forceAll = qe.forceAll;
16559         if(forceAll === true || (q.length >= this.minChars)){
16560             
16561             this.hasQuery = true;
16562             
16563             if(this.lastQuery != q || this.alwaysQuery){
16564                 this.lastQuery = q;
16565                 if(this.mode == 'local'){
16566                     this.selectedIndex = -1;
16567                     if(forceAll){
16568                         this.store.clearFilter();
16569                     }else{
16570                         
16571                         if(this.specialFilter){
16572                             this.fireEvent('specialfilter', this);
16573                             this.onLoad();
16574                             return;
16575                         }
16576                         
16577                         this.store.filter(this.displayField, q);
16578                     }
16579                     
16580                     this.store.fireEvent("datachanged", this.store);
16581                     
16582                     this.onLoad();
16583                     
16584                     
16585                 }else{
16586                     
16587                     this.store.baseParams[this.queryParam] = q;
16588                     
16589                     var options = {params : this.getParams(q)};
16590                     
16591                     if(this.loadNext){
16592                         options.add = true;
16593                         options.params.start = this.page * this.pageSize;
16594                     }
16595                     
16596                     this.store.load(options);
16597                     
16598                     /*
16599                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16600                      *  we should expand the list on onLoad
16601                      *  so command out it
16602                      */
16603 //                    this.expand();
16604                 }
16605             }else{
16606                 this.selectedIndex = -1;
16607                 this.onLoad();   
16608             }
16609         }
16610         
16611         this.loadNext = false;
16612     },
16613     
16614     // private
16615     getParams : function(q){
16616         var p = {};
16617         //p[this.queryParam] = q;
16618         
16619         if(this.pageSize){
16620             p.start = 0;
16621             p.limit = this.pageSize;
16622         }
16623         return p;
16624     },
16625
16626     /**
16627      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16628      */
16629     collapse : function(){
16630         if(!this.isExpanded()){
16631             return;
16632         }
16633         
16634         this.list.hide();
16635         
16636         this.hasFocus = false;
16637         
16638         if(this.tickable){
16639             this.okBtn.hide();
16640             this.cancelBtn.hide();
16641             this.trigger.show();
16642             
16643             if(this.editable){
16644                 this.tickableInputEl().dom.value = '';
16645                 this.tickableInputEl().blur();
16646             }
16647             
16648         }
16649         
16650         Roo.get(document).un('mousedown', this.collapseIf, this);
16651         Roo.get(document).un('mousewheel', this.collapseIf, this);
16652         if (!this.editable) {
16653             Roo.get(document).un('keydown', this.listKeyPress, this);
16654         }
16655         this.fireEvent('collapse', this);
16656         
16657         this.validate();
16658     },
16659
16660     // private
16661     collapseIf : function(e){
16662         var in_combo  = e.within(this.el);
16663         var in_list =  e.within(this.list);
16664         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16665         
16666         if (in_combo || in_list || is_list) {
16667             //e.stopPropagation();
16668             return;
16669         }
16670         
16671         if(this.tickable){
16672             this.onTickableFooterButtonClick(e, false, false);
16673         }
16674
16675         this.collapse();
16676         
16677     },
16678
16679     /**
16680      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16681      */
16682     expand : function(){
16683        
16684         if(this.isExpanded() || !this.hasFocus){
16685             return;
16686         }
16687         
16688         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16689         this.list.setWidth(lw);
16690         
16691         Roo.log('expand');
16692         
16693         this.list.show();
16694         
16695         this.restrictHeight();
16696         
16697         if(this.tickable){
16698             
16699             this.tickItems = Roo.apply([], this.item);
16700             
16701             this.okBtn.show();
16702             this.cancelBtn.show();
16703             this.trigger.hide();
16704             
16705             if(this.editable){
16706                 this.tickableInputEl().focus();
16707             }
16708             
16709         }
16710         
16711         Roo.get(document).on('mousedown', this.collapseIf, this);
16712         Roo.get(document).on('mousewheel', this.collapseIf, this);
16713         if (!this.editable) {
16714             Roo.get(document).on('keydown', this.listKeyPress, this);
16715         }
16716         
16717         this.fireEvent('expand', this);
16718     },
16719
16720     // private
16721     // Implements the default empty TriggerField.onTriggerClick function
16722     onTriggerClick : function(e)
16723     {
16724         Roo.log('trigger click');
16725         
16726         if(this.disabled || !this.triggerList){
16727             return;
16728         }
16729         
16730         this.page = 0;
16731         this.loadNext = false;
16732         
16733         if(this.isExpanded()){
16734             this.collapse();
16735             if (!this.blockFocus) {
16736                 this.inputEl().focus();
16737             }
16738             
16739         }else {
16740             this.hasFocus = true;
16741             if(this.triggerAction == 'all') {
16742                 this.doQuery(this.allQuery, true);
16743             } else {
16744                 this.doQuery(this.getRawValue());
16745             }
16746             if (!this.blockFocus) {
16747                 this.inputEl().focus();
16748             }
16749         }
16750     },
16751     
16752     onTickableTriggerClick : function(e)
16753     {
16754         if(this.disabled){
16755             return;
16756         }
16757         
16758         this.page = 0;
16759         this.loadNext = false;
16760         this.hasFocus = true;
16761         
16762         if(this.triggerAction == 'all') {
16763             this.doQuery(this.allQuery, true);
16764         } else {
16765             this.doQuery(this.getRawValue());
16766         }
16767     },
16768     
16769     onSearchFieldClick : function(e)
16770     {
16771         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16772             this.onTickableFooterButtonClick(e, false, false);
16773             return;
16774         }
16775         
16776         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16777             return;
16778         }
16779         
16780         this.page = 0;
16781         this.loadNext = false;
16782         this.hasFocus = true;
16783         
16784         if(this.triggerAction == 'all') {
16785             this.doQuery(this.allQuery, true);
16786         } else {
16787             this.doQuery(this.getRawValue());
16788         }
16789     },
16790     
16791     listKeyPress : function(e)
16792     {
16793         //Roo.log('listkeypress');
16794         // scroll to first matching element based on key pres..
16795         if (e.isSpecialKey()) {
16796             return false;
16797         }
16798         var k = String.fromCharCode(e.getKey()).toUpperCase();
16799         //Roo.log(k);
16800         var match  = false;
16801         var csel = this.view.getSelectedNodes();
16802         var cselitem = false;
16803         if (csel.length) {
16804             var ix = this.view.indexOf(csel[0]);
16805             cselitem  = this.store.getAt(ix);
16806             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16807                 cselitem = false;
16808             }
16809             
16810         }
16811         
16812         this.store.each(function(v) { 
16813             if (cselitem) {
16814                 // start at existing selection.
16815                 if (cselitem.id == v.id) {
16816                     cselitem = false;
16817                 }
16818                 return true;
16819             }
16820                 
16821             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16822                 match = this.store.indexOf(v);
16823                 return false;
16824             }
16825             return true;
16826         }, this);
16827         
16828         if (match === false) {
16829             return true; // no more action?
16830         }
16831         // scroll to?
16832         this.view.select(match);
16833         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16834         sn.scrollIntoView(sn.dom.parentNode, false);
16835     },
16836     
16837     onViewScroll : function(e, t){
16838         
16839         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){
16840             return;
16841         }
16842         
16843         this.hasQuery = true;
16844         
16845         this.loading = this.list.select('.loading', true).first();
16846         
16847         if(this.loading === null){
16848             this.list.createChild({
16849                 tag: 'div',
16850                 cls: 'loading roo-select2-more-results roo-select2-active',
16851                 html: 'Loading more results...'
16852             });
16853             
16854             this.loading = this.list.select('.loading', true).first();
16855             
16856             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16857             
16858             this.loading.hide();
16859         }
16860         
16861         this.loading.show();
16862         
16863         var _combo = this;
16864         
16865         this.page++;
16866         this.loadNext = true;
16867         
16868         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16869         
16870         return;
16871     },
16872     
16873     addItem : function(o)
16874     {   
16875         var dv = ''; // display value
16876         
16877         if (this.displayField) {
16878             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16879         } else {
16880             // this is an error condition!!!
16881             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16882         }
16883         
16884         if(!dv.length){
16885             return;
16886         }
16887         
16888         var choice = this.choices.createChild({
16889             tag: 'li',
16890             cls: 'roo-select2-search-choice',
16891             cn: [
16892                 {
16893                     tag: 'div',
16894                     html: dv
16895                 },
16896                 {
16897                     tag: 'a',
16898                     href: '#',
16899                     cls: 'roo-select2-search-choice-close fa fa-times',
16900                     tabindex: '-1'
16901                 }
16902             ]
16903             
16904         }, this.searchField);
16905         
16906         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16907         
16908         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16909         
16910         this.item.push(o);
16911         
16912         this.lastData = o;
16913         
16914         this.syncValue();
16915         
16916         this.inputEl().dom.value = '';
16917         
16918         this.validate();
16919     },
16920     
16921     onRemoveItem : function(e, _self, o)
16922     {
16923         e.preventDefault();
16924         
16925         this.lastItem = Roo.apply([], this.item);
16926         
16927         var index = this.item.indexOf(o.data) * 1;
16928         
16929         if( index < 0){
16930             Roo.log('not this item?!');
16931             return;
16932         }
16933         
16934         this.item.splice(index, 1);
16935         o.item.remove();
16936         
16937         this.syncValue();
16938         
16939         this.fireEvent('remove', this, e);
16940         
16941         this.validate();
16942         
16943     },
16944     
16945     syncValue : function()
16946     {
16947         if(!this.item.length){
16948             this.clearValue();
16949             return;
16950         }
16951             
16952         var value = [];
16953         var _this = this;
16954         Roo.each(this.item, function(i){
16955             if(_this.valueField){
16956                 value.push(i[_this.valueField]);
16957                 return;
16958             }
16959
16960             value.push(i);
16961         });
16962
16963         this.value = value.join(',');
16964
16965         if(this.hiddenField){
16966             this.hiddenField.dom.value = this.value;
16967         }
16968         
16969         this.store.fireEvent("datachanged", this.store);
16970         
16971         this.validate();
16972     },
16973     
16974     clearItem : function()
16975     {
16976         if(!this.multiple){
16977             return;
16978         }
16979         
16980         this.item = [];
16981         
16982         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16983            c.remove();
16984         });
16985         
16986         this.syncValue();
16987         
16988         this.validate();
16989         
16990         if(this.tickable && !Roo.isTouch){
16991             this.view.refresh();
16992         }
16993     },
16994     
16995     inputEl: function ()
16996     {
16997         if(Roo.isIOS && this.useNativeIOS){
16998             return this.el.select('select.roo-ios-select', true).first();
16999         }
17000         
17001         if(Roo.isTouch && this.mobileTouchView){
17002             return this.el.select('input.form-control',true).first();
17003         }
17004         
17005         if(this.tickable){
17006             return this.searchField;
17007         }
17008         
17009         return this.el.select('input.form-control',true).first();
17010     },
17011     
17012     onTickableFooterButtonClick : function(e, btn, el)
17013     {
17014         e.preventDefault();
17015         
17016         this.lastItem = Roo.apply([], this.item);
17017         
17018         if(btn && btn.name == 'cancel'){
17019             this.tickItems = Roo.apply([], this.item);
17020             this.collapse();
17021             return;
17022         }
17023         
17024         this.clearItem();
17025         
17026         var _this = this;
17027         
17028         Roo.each(this.tickItems, function(o){
17029             _this.addItem(o);
17030         });
17031         
17032         this.collapse();
17033         
17034     },
17035     
17036     validate : function()
17037     {
17038         if(this.getVisibilityEl().hasClass('hidden')){
17039             return true;
17040         }
17041         
17042         var v = this.getRawValue();
17043         
17044         if(this.multiple){
17045             v = this.getValue();
17046         }
17047         
17048         if(this.disabled || this.allowBlank || v.length){
17049             this.markValid();
17050             return true;
17051         }
17052         
17053         this.markInvalid();
17054         return false;
17055     },
17056     
17057     tickableInputEl : function()
17058     {
17059         if(!this.tickable || !this.editable){
17060             return this.inputEl();
17061         }
17062         
17063         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17064     },
17065     
17066     
17067     getAutoCreateTouchView : function()
17068     {
17069         var id = Roo.id();
17070         
17071         var cfg = {
17072             cls: 'form-group' //input-group
17073         };
17074         
17075         var input =  {
17076             tag: 'input',
17077             id : id,
17078             type : this.inputType,
17079             cls : 'form-control x-combo-noedit',
17080             autocomplete: 'new-password',
17081             placeholder : this.placeholder || '',
17082             readonly : true
17083         };
17084         
17085         if (this.name) {
17086             input.name = this.name;
17087         }
17088         
17089         if (this.size) {
17090             input.cls += ' input-' + this.size;
17091         }
17092         
17093         if (this.disabled) {
17094             input.disabled = true;
17095         }
17096         
17097         var inputblock = {
17098             cls : 'roo-combobox-wrap',
17099             cn : [
17100                 input
17101             ]
17102         };
17103         
17104         if(this.before){
17105             inputblock.cls += ' input-group';
17106             
17107             inputblock.cn.unshift({
17108                 tag :'span',
17109                 cls : 'input-group-addon input-group-prepend input-group-text',
17110                 html : this.before
17111             });
17112         }
17113         
17114         if(this.removable && !this.multiple){
17115             inputblock.cls += ' roo-removable';
17116             
17117             inputblock.cn.push({
17118                 tag: 'button',
17119                 html : 'x',
17120                 cls : 'roo-combo-removable-btn close'
17121             });
17122         }
17123
17124         if(this.hasFeedback && !this.allowBlank){
17125             
17126             inputblock.cls += ' has-feedback';
17127             
17128             inputblock.cn.push({
17129                 tag: 'span',
17130                 cls: 'glyphicon form-control-feedback'
17131             });
17132             
17133         }
17134         
17135         if (this.after) {
17136             
17137             inputblock.cls += (this.before) ? '' : ' input-group';
17138             
17139             inputblock.cn.push({
17140                 tag :'span',
17141                 cls : 'input-group-addon input-group-append input-group-text',
17142                 html : this.after
17143             });
17144         }
17145
17146         
17147         var ibwrap = inputblock;
17148         
17149         if(this.multiple){
17150             ibwrap = {
17151                 tag: 'ul',
17152                 cls: 'roo-select2-choices',
17153                 cn:[
17154                     {
17155                         tag: 'li',
17156                         cls: 'roo-select2-search-field',
17157                         cn: [
17158
17159                             inputblock
17160                         ]
17161                     }
17162                 ]
17163             };
17164         
17165             
17166         }
17167         
17168         var combobox = {
17169             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17170             cn: [
17171                 {
17172                     tag: 'input',
17173                     type : 'hidden',
17174                     cls: 'form-hidden-field'
17175                 },
17176                 ibwrap
17177             ]
17178         };
17179         
17180         if(!this.multiple && this.showToggleBtn){
17181             
17182             var caret = {
17183                 cls: 'caret'
17184             };
17185             
17186             if (this.caret != false) {
17187                 caret = {
17188                      tag: 'i',
17189                      cls: 'fa fa-' + this.caret
17190                 };
17191                 
17192             }
17193             
17194             combobox.cn.push({
17195                 tag :'span',
17196                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17197                 cn : [
17198                     Roo.bootstrap.version == 3 ? caret : '',
17199                     {
17200                         tag: 'span',
17201                         cls: 'combobox-clear',
17202                         cn  : [
17203                             {
17204                                 tag : 'i',
17205                                 cls: 'icon-remove'
17206                             }
17207                         ]
17208                     }
17209                 ]
17210
17211             })
17212         }
17213         
17214         if(this.multiple){
17215             combobox.cls += ' roo-select2-container-multi';
17216         }
17217         
17218         var align = this.labelAlign || this.parentLabelAlign();
17219         
17220         if (align ==='left' && this.fieldLabel.length) {
17221
17222             cfg.cn = [
17223                 {
17224                    tag : 'i',
17225                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17226                    tooltip : 'This field is required'
17227                 },
17228                 {
17229                     tag: 'label',
17230                     cls : 'control-label col-form-label',
17231                     html : this.fieldLabel
17232
17233                 },
17234                 {
17235                     cls : 'roo-combobox-wrap ', 
17236                     cn: [
17237                         combobox
17238                     ]
17239                 }
17240             ];
17241             
17242             var labelCfg = cfg.cn[1];
17243             var contentCfg = cfg.cn[2];
17244             
17245
17246             if(this.indicatorpos == 'right'){
17247                 cfg.cn = [
17248                     {
17249                         tag: 'label',
17250                         'for' :  id,
17251                         cls : 'control-label col-form-label',
17252                         cn : [
17253                             {
17254                                 tag : 'span',
17255                                 html : this.fieldLabel
17256                             },
17257                             {
17258                                 tag : 'i',
17259                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17260                                 tooltip : 'This field is required'
17261                             }
17262                         ]
17263                     },
17264                     {
17265                         cls : "roo-combobox-wrap ",
17266                         cn: [
17267                             combobox
17268                         ]
17269                     }
17270
17271                 ];
17272                 
17273                 labelCfg = cfg.cn[0];
17274                 contentCfg = cfg.cn[1];
17275             }
17276             
17277            
17278             
17279             if(this.labelWidth > 12){
17280                 labelCfg.style = "width: " + this.labelWidth + 'px';
17281             }
17282            
17283             if(this.labelWidth < 13 && this.labelmd == 0){
17284                 this.labelmd = this.labelWidth;
17285             }
17286             
17287             if(this.labellg > 0){
17288                 labelCfg.cls += ' col-lg-' + this.labellg;
17289                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17290             }
17291             
17292             if(this.labelmd > 0){
17293                 labelCfg.cls += ' col-md-' + this.labelmd;
17294                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17295             }
17296             
17297             if(this.labelsm > 0){
17298                 labelCfg.cls += ' col-sm-' + this.labelsm;
17299                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17300             }
17301             
17302             if(this.labelxs > 0){
17303                 labelCfg.cls += ' col-xs-' + this.labelxs;
17304                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17305             }
17306                 
17307                 
17308         } else if ( this.fieldLabel.length) {
17309             cfg.cn = [
17310                 {
17311                    tag : 'i',
17312                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17313                    tooltip : 'This field is required'
17314                 },
17315                 {
17316                     tag: 'label',
17317                     cls : 'control-label',
17318                     html : this.fieldLabel
17319
17320                 },
17321                 {
17322                     cls : '', 
17323                     cn: [
17324                         combobox
17325                     ]
17326                 }
17327             ];
17328             
17329             if(this.indicatorpos == 'right'){
17330                 cfg.cn = [
17331                     {
17332                         tag: 'label',
17333                         cls : 'control-label',
17334                         html : this.fieldLabel,
17335                         cn : [
17336                             {
17337                                tag : 'i',
17338                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17339                                tooltip : 'This field is required'
17340                             }
17341                         ]
17342                     },
17343                     {
17344                         cls : '', 
17345                         cn: [
17346                             combobox
17347                         ]
17348                     }
17349                 ];
17350             }
17351         } else {
17352             cfg.cn = combobox;    
17353         }
17354         
17355         
17356         var settings = this;
17357         
17358         ['xs','sm','md','lg'].map(function(size){
17359             if (settings[size]) {
17360                 cfg.cls += ' col-' + size + '-' + settings[size];
17361             }
17362         });
17363         
17364         return cfg;
17365     },
17366     
17367     initTouchView : function()
17368     {
17369         this.renderTouchView();
17370         
17371         this.touchViewEl.on('scroll', function(){
17372             this.el.dom.scrollTop = 0;
17373         }, this);
17374         
17375         this.originalValue = this.getValue();
17376         
17377         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17378         
17379         this.inputEl().on("click", this.showTouchView, this);
17380         if (this.triggerEl) {
17381             this.triggerEl.on("click", this.showTouchView, this);
17382         }
17383         
17384         
17385         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17386         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17387         
17388         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17389         
17390         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17391         this.store.on('load', this.onTouchViewLoad, this);
17392         this.store.on('loadexception', this.onTouchViewLoadException, this);
17393         
17394         if(this.hiddenName){
17395             
17396             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17397             
17398             this.hiddenField.dom.value =
17399                 this.hiddenValue !== undefined ? this.hiddenValue :
17400                 this.value !== undefined ? this.value : '';
17401         
17402             this.el.dom.removeAttribute('name');
17403             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17404         }
17405         
17406         if(this.multiple){
17407             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17408             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17409         }
17410         
17411         if(this.removable && !this.multiple){
17412             var close = this.closeTriggerEl();
17413             if(close){
17414                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17415                 close.on('click', this.removeBtnClick, this, close);
17416             }
17417         }
17418         /*
17419          * fix the bug in Safari iOS8
17420          */
17421         this.inputEl().on("focus", function(e){
17422             document.activeElement.blur();
17423         }, this);
17424         
17425         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17426         
17427         return;
17428         
17429         
17430     },
17431     
17432     renderTouchView : function()
17433     {
17434         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17435         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17436         
17437         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17438         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17439         
17440         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17441         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17442         this.touchViewBodyEl.setStyle('overflow', 'auto');
17443         
17444         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17445         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17446         
17447         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17448         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17449         
17450     },
17451     
17452     showTouchView : function()
17453     {
17454         if(this.disabled){
17455             return;
17456         }
17457         
17458         this.touchViewHeaderEl.hide();
17459
17460         if(this.modalTitle.length){
17461             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17462             this.touchViewHeaderEl.show();
17463         }
17464
17465         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17466         this.touchViewEl.show();
17467
17468         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17469         
17470         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17471         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17472
17473         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17474
17475         if(this.modalTitle.length){
17476             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17477         }
17478         
17479         this.touchViewBodyEl.setHeight(bodyHeight);
17480
17481         if(this.animate){
17482             var _this = this;
17483             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17484         }else{
17485             this.touchViewEl.addClass(['in','show']);
17486         }
17487         
17488         if(this._touchViewMask){
17489             Roo.get(document.body).addClass("x-body-masked");
17490             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17491             this._touchViewMask.setStyle('z-index', 10000);
17492             this._touchViewMask.addClass('show');
17493         }
17494         
17495         this.doTouchViewQuery();
17496         
17497     },
17498     
17499     hideTouchView : function()
17500     {
17501         this.touchViewEl.removeClass(['in','show']);
17502
17503         if(this.animate){
17504             var _this = this;
17505             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17506         }else{
17507             this.touchViewEl.setStyle('display', 'none');
17508         }
17509         
17510         if(this._touchViewMask){
17511             this._touchViewMask.removeClass('show');
17512             Roo.get(document.body).removeClass("x-body-masked");
17513         }
17514     },
17515     
17516     setTouchViewValue : function()
17517     {
17518         if(this.multiple){
17519             this.clearItem();
17520         
17521             var _this = this;
17522
17523             Roo.each(this.tickItems, function(o){
17524                 this.addItem(o);
17525             }, this);
17526         }
17527         
17528         this.hideTouchView();
17529     },
17530     
17531     doTouchViewQuery : function()
17532     {
17533         var qe = {
17534             query: '',
17535             forceAll: true,
17536             combo: this,
17537             cancel:false
17538         };
17539         
17540         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17541             return false;
17542         }
17543         
17544         if(!this.alwaysQuery || this.mode == 'local'){
17545             this.onTouchViewLoad();
17546             return;
17547         }
17548         
17549         this.store.load();
17550     },
17551     
17552     onTouchViewBeforeLoad : function(combo,opts)
17553     {
17554         return;
17555     },
17556
17557     // private
17558     onTouchViewLoad : function()
17559     {
17560         if(this.store.getCount() < 1){
17561             this.onTouchViewEmptyResults();
17562             return;
17563         }
17564         
17565         this.clearTouchView();
17566         
17567         var rawValue = this.getRawValue();
17568         
17569         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17570         
17571         this.tickItems = [];
17572         
17573         this.store.data.each(function(d, rowIndex){
17574             var row = this.touchViewListGroup.createChild(template);
17575             
17576             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17577                 row.addClass(d.data.cls);
17578             }
17579             
17580             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17581                 var cfg = {
17582                     data : d.data,
17583                     html : d.data[this.displayField]
17584                 };
17585                 
17586                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17587                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17588                 }
17589             }
17590             row.removeClass('selected');
17591             if(!this.multiple && this.valueField &&
17592                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17593             {
17594                 // radio buttons..
17595                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17596                 row.addClass('selected');
17597             }
17598             
17599             if(this.multiple && this.valueField &&
17600                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17601             {
17602                 
17603                 // checkboxes...
17604                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17605                 this.tickItems.push(d.data);
17606             }
17607             
17608             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17609             
17610         }, this);
17611         
17612         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17613         
17614         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17615
17616         if(this.modalTitle.length){
17617             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17618         }
17619
17620         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17621         
17622         if(this.mobile_restrict_height && listHeight < bodyHeight){
17623             this.touchViewBodyEl.setHeight(listHeight);
17624         }
17625         
17626         var _this = this;
17627         
17628         if(firstChecked && listHeight > bodyHeight){
17629             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17630         }
17631         
17632     },
17633     
17634     onTouchViewLoadException : function()
17635     {
17636         this.hideTouchView();
17637     },
17638     
17639     onTouchViewEmptyResults : function()
17640     {
17641         this.clearTouchView();
17642         
17643         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17644         
17645         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17646         
17647     },
17648     
17649     clearTouchView : function()
17650     {
17651         this.touchViewListGroup.dom.innerHTML = '';
17652     },
17653     
17654     onTouchViewClick : function(e, el, o)
17655     {
17656         e.preventDefault();
17657         
17658         var row = o.row;
17659         var rowIndex = o.rowIndex;
17660         
17661         var r = this.store.getAt(rowIndex);
17662         
17663         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17664             
17665             if(!this.multiple){
17666                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17667                     c.dom.removeAttribute('checked');
17668                 }, this);
17669
17670                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17671
17672                 this.setFromData(r.data);
17673
17674                 var close = this.closeTriggerEl();
17675
17676                 if(close){
17677                     close.show();
17678                 }
17679
17680                 this.hideTouchView();
17681
17682                 this.fireEvent('select', this, r, rowIndex);
17683
17684                 return;
17685             }
17686
17687             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17688                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17689                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17690                 return;
17691             }
17692
17693             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17694             this.addItem(r.data);
17695             this.tickItems.push(r.data);
17696         }
17697     },
17698     
17699     getAutoCreateNativeIOS : function()
17700     {
17701         var cfg = {
17702             cls: 'form-group' //input-group,
17703         };
17704         
17705         var combobox =  {
17706             tag: 'select',
17707             cls : 'roo-ios-select'
17708         };
17709         
17710         if (this.name) {
17711             combobox.name = this.name;
17712         }
17713         
17714         if (this.disabled) {
17715             combobox.disabled = true;
17716         }
17717         
17718         var settings = this;
17719         
17720         ['xs','sm','md','lg'].map(function(size){
17721             if (settings[size]) {
17722                 cfg.cls += ' col-' + size + '-' + settings[size];
17723             }
17724         });
17725         
17726         cfg.cn = combobox;
17727         
17728         return cfg;
17729         
17730     },
17731     
17732     initIOSView : function()
17733     {
17734         this.store.on('load', this.onIOSViewLoad, this);
17735         
17736         return;
17737     },
17738     
17739     onIOSViewLoad : function()
17740     {
17741         if(this.store.getCount() < 1){
17742             return;
17743         }
17744         
17745         this.clearIOSView();
17746         
17747         if(this.allowBlank) {
17748             
17749             var default_text = '-- SELECT --';
17750             
17751             if(this.placeholder.length){
17752                 default_text = this.placeholder;
17753             }
17754             
17755             if(this.emptyTitle.length){
17756                 default_text += ' - ' + this.emptyTitle + ' -';
17757             }
17758             
17759             var opt = this.inputEl().createChild({
17760                 tag: 'option',
17761                 value : 0,
17762                 html : default_text
17763             });
17764             
17765             var o = {};
17766             o[this.valueField] = 0;
17767             o[this.displayField] = default_text;
17768             
17769             this.ios_options.push({
17770                 data : o,
17771                 el : opt
17772             });
17773             
17774         }
17775         
17776         this.store.data.each(function(d, rowIndex){
17777             
17778             var html = '';
17779             
17780             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17781                 html = d.data[this.displayField];
17782             }
17783             
17784             var value = '';
17785             
17786             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17787                 value = d.data[this.valueField];
17788             }
17789             
17790             var option = {
17791                 tag: 'option',
17792                 value : value,
17793                 html : html
17794             };
17795             
17796             if(this.value == d.data[this.valueField]){
17797                 option['selected'] = true;
17798             }
17799             
17800             var opt = this.inputEl().createChild(option);
17801             
17802             this.ios_options.push({
17803                 data : d.data,
17804                 el : opt
17805             });
17806             
17807         }, this);
17808         
17809         this.inputEl().on('change', function(){
17810            this.fireEvent('select', this);
17811         }, this);
17812         
17813     },
17814     
17815     clearIOSView: function()
17816     {
17817         this.inputEl().dom.innerHTML = '';
17818         
17819         this.ios_options = [];
17820     },
17821     
17822     setIOSValue: function(v)
17823     {
17824         this.value = v;
17825         
17826         if(!this.ios_options){
17827             return;
17828         }
17829         
17830         Roo.each(this.ios_options, function(opts){
17831            
17832            opts.el.dom.removeAttribute('selected');
17833            
17834            if(opts.data[this.valueField] != v){
17835                return;
17836            }
17837            
17838            opts.el.dom.setAttribute('selected', true);
17839            
17840         }, this);
17841     }
17842
17843     /** 
17844     * @cfg {Boolean} grow 
17845     * @hide 
17846     */
17847     /** 
17848     * @cfg {Number} growMin 
17849     * @hide 
17850     */
17851     /** 
17852     * @cfg {Number} growMax 
17853     * @hide 
17854     */
17855     /**
17856      * @hide
17857      * @method autoSize
17858      */
17859 });
17860
17861 Roo.apply(Roo.bootstrap.ComboBox,  {
17862     
17863     header : {
17864         tag: 'div',
17865         cls: 'modal-header',
17866         cn: [
17867             {
17868                 tag: 'h4',
17869                 cls: 'modal-title'
17870             }
17871         ]
17872     },
17873     
17874     body : {
17875         tag: 'div',
17876         cls: 'modal-body',
17877         cn: [
17878             {
17879                 tag: 'ul',
17880                 cls: 'list-group'
17881             }
17882         ]
17883     },
17884     
17885     listItemRadio : {
17886         tag: 'li',
17887         cls: 'list-group-item',
17888         cn: [
17889             {
17890                 tag: 'span',
17891                 cls: 'roo-combobox-list-group-item-value'
17892             },
17893             {
17894                 tag: 'div',
17895                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17896                 cn: [
17897                     {
17898                         tag: 'input',
17899                         type: 'radio'
17900                     },
17901                     {
17902                         tag: 'label'
17903                     }
17904                 ]
17905             }
17906         ]
17907     },
17908     
17909     listItemCheckbox : {
17910         tag: 'li',
17911         cls: 'list-group-item',
17912         cn: [
17913             {
17914                 tag: 'span',
17915                 cls: 'roo-combobox-list-group-item-value'
17916             },
17917             {
17918                 tag: 'div',
17919                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17920                 cn: [
17921                     {
17922                         tag: 'input',
17923                         type: 'checkbox'
17924                     },
17925                     {
17926                         tag: 'label'
17927                     }
17928                 ]
17929             }
17930         ]
17931     },
17932     
17933     emptyResult : {
17934         tag: 'div',
17935         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17936     },
17937     
17938     footer : {
17939         tag: 'div',
17940         cls: 'modal-footer',
17941         cn: [
17942             {
17943                 tag: 'div',
17944                 cls: 'row',
17945                 cn: [
17946                     {
17947                         tag: 'div',
17948                         cls: 'col-xs-6 text-left',
17949                         cn: {
17950                             tag: 'button',
17951                             cls: 'btn btn-danger roo-touch-view-cancel',
17952                             html: 'Cancel'
17953                         }
17954                     },
17955                     {
17956                         tag: 'div',
17957                         cls: 'col-xs-6 text-right',
17958                         cn: {
17959                             tag: 'button',
17960                             cls: 'btn btn-success roo-touch-view-ok',
17961                             html: 'OK'
17962                         }
17963                     }
17964                 ]
17965             }
17966         ]
17967         
17968     }
17969 });
17970
17971 Roo.apply(Roo.bootstrap.ComboBox,  {
17972     
17973     touchViewTemplate : {
17974         tag: 'div',
17975         cls: 'modal fade roo-combobox-touch-view',
17976         cn: [
17977             {
17978                 tag: 'div',
17979                 cls: 'modal-dialog',
17980                 style : 'position:fixed', // we have to fix position....
17981                 cn: [
17982                     {
17983                         tag: 'div',
17984                         cls: 'modal-content',
17985                         cn: [
17986                             Roo.bootstrap.ComboBox.header,
17987                             Roo.bootstrap.ComboBox.body,
17988                             Roo.bootstrap.ComboBox.footer
17989                         ]
17990                     }
17991                 ]
17992             }
17993         ]
17994     }
17995 });/*
17996  * Based on:
17997  * Ext JS Library 1.1.1
17998  * Copyright(c) 2006-2007, Ext JS, LLC.
17999  *
18000  * Originally Released Under LGPL - original licence link has changed is not relivant.
18001  *
18002  * Fork - LGPL
18003  * <script type="text/javascript">
18004  */
18005
18006 /**
18007  * @class Roo.View
18008  * @extends Roo.util.Observable
18009  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18010  * This class also supports single and multi selection modes. <br>
18011  * Create a data model bound view:
18012  <pre><code>
18013  var store = new Roo.data.Store(...);
18014
18015  var view = new Roo.View({
18016     el : "my-element",
18017     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18018  
18019     singleSelect: true,
18020     selectedClass: "ydataview-selected",
18021     store: store
18022  });
18023
18024  // listen for node click?
18025  view.on("click", function(vw, index, node, e){
18026  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18027  });
18028
18029  // load XML data
18030  dataModel.load("foobar.xml");
18031  </code></pre>
18032  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18033  * <br><br>
18034  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18035  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18036  * 
18037  * Note: old style constructor is still suported (container, template, config)
18038  * 
18039  * @constructor
18040  * Create a new View
18041  * @param {Object} config The config object
18042  * 
18043  */
18044 Roo.View = function(config, depreciated_tpl, depreciated_config){
18045     
18046     this.parent = false;
18047     
18048     if (typeof(depreciated_tpl) == 'undefined') {
18049         // new way.. - universal constructor.
18050         Roo.apply(this, config);
18051         this.el  = Roo.get(this.el);
18052     } else {
18053         // old format..
18054         this.el  = Roo.get(config);
18055         this.tpl = depreciated_tpl;
18056         Roo.apply(this, depreciated_config);
18057     }
18058     this.wrapEl  = this.el.wrap().wrap();
18059     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18060     
18061     
18062     if(typeof(this.tpl) == "string"){
18063         this.tpl = new Roo.Template(this.tpl);
18064     } else {
18065         // support xtype ctors..
18066         this.tpl = new Roo.factory(this.tpl, Roo);
18067     }
18068     
18069     
18070     this.tpl.compile();
18071     
18072     /** @private */
18073     this.addEvents({
18074         /**
18075          * @event beforeclick
18076          * Fires before a click is processed. Returns false to cancel the default action.
18077          * @param {Roo.View} this
18078          * @param {Number} index The index of the target node
18079          * @param {HTMLElement} node The target node
18080          * @param {Roo.EventObject} e The raw event object
18081          */
18082             "beforeclick" : true,
18083         /**
18084          * @event click
18085          * Fires when a template node is clicked.
18086          * @param {Roo.View} this
18087          * @param {Number} index The index of the target node
18088          * @param {HTMLElement} node The target node
18089          * @param {Roo.EventObject} e The raw event object
18090          */
18091             "click" : true,
18092         /**
18093          * @event dblclick
18094          * Fires when a template node is double clicked.
18095          * @param {Roo.View} this
18096          * @param {Number} index The index of the target node
18097          * @param {HTMLElement} node The target node
18098          * @param {Roo.EventObject} e The raw event object
18099          */
18100             "dblclick" : true,
18101         /**
18102          * @event contextmenu
18103          * Fires when a template node is right clicked.
18104          * @param {Roo.View} this
18105          * @param {Number} index The index of the target node
18106          * @param {HTMLElement} node The target node
18107          * @param {Roo.EventObject} e The raw event object
18108          */
18109             "contextmenu" : true,
18110         /**
18111          * @event selectionchange
18112          * Fires when the selected nodes change.
18113          * @param {Roo.View} this
18114          * @param {Array} selections Array of the selected nodes
18115          */
18116             "selectionchange" : true,
18117     
18118         /**
18119          * @event beforeselect
18120          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18121          * @param {Roo.View} this
18122          * @param {HTMLElement} node The node to be selected
18123          * @param {Array} selections Array of currently selected nodes
18124          */
18125             "beforeselect" : true,
18126         /**
18127          * @event preparedata
18128          * Fires on every row to render, to allow you to change the data.
18129          * @param {Roo.View} this
18130          * @param {Object} data to be rendered (change this)
18131          */
18132           "preparedata" : true
18133           
18134           
18135         });
18136
18137
18138
18139     this.el.on({
18140         "click": this.onClick,
18141         "dblclick": this.onDblClick,
18142         "contextmenu": this.onContextMenu,
18143         scope:this
18144     });
18145
18146     this.selections = [];
18147     this.nodes = [];
18148     this.cmp = new Roo.CompositeElementLite([]);
18149     if(this.store){
18150         this.store = Roo.factory(this.store, Roo.data);
18151         this.setStore(this.store, true);
18152     }
18153     
18154     if ( this.footer && this.footer.xtype) {
18155            
18156          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18157         
18158         this.footer.dataSource = this.store;
18159         this.footer.container = fctr;
18160         this.footer = Roo.factory(this.footer, Roo);
18161         fctr.insertFirst(this.el);
18162         
18163         // this is a bit insane - as the paging toolbar seems to detach the el..
18164 //        dom.parentNode.parentNode.parentNode
18165          // they get detached?
18166     }
18167     
18168     
18169     Roo.View.superclass.constructor.call(this);
18170     
18171     
18172 };
18173
18174 Roo.extend(Roo.View, Roo.util.Observable, {
18175     
18176      /**
18177      * @cfg {Roo.data.Store} store Data store to load data from.
18178      */
18179     store : false,
18180     
18181     /**
18182      * @cfg {String|Roo.Element} el The container element.
18183      */
18184     el : '',
18185     
18186     /**
18187      * @cfg {String|Roo.Template} tpl The template used by this View 
18188      */
18189     tpl : false,
18190     /**
18191      * @cfg {String} dataName the named area of the template to use as the data area
18192      *                          Works with domtemplates roo-name="name"
18193      */
18194     dataName: false,
18195     /**
18196      * @cfg {String} selectedClass The css class to add to selected nodes
18197      */
18198     selectedClass : "x-view-selected",
18199      /**
18200      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18201      */
18202     emptyText : "",
18203     
18204     /**
18205      * @cfg {String} text to display on mask (default Loading)
18206      */
18207     mask : false,
18208     /**
18209      * @cfg {Boolean} multiSelect Allow multiple selection
18210      */
18211     multiSelect : false,
18212     /**
18213      * @cfg {Boolean} singleSelect Allow single selection
18214      */
18215     singleSelect:  false,
18216     
18217     /**
18218      * @cfg {Boolean} toggleSelect - selecting 
18219      */
18220     toggleSelect : false,
18221     
18222     /**
18223      * @cfg {Boolean} tickable - selecting 
18224      */
18225     tickable : false,
18226     
18227     /**
18228      * Returns the element this view is bound to.
18229      * @return {Roo.Element}
18230      */
18231     getEl : function(){
18232         return this.wrapEl;
18233     },
18234     
18235     
18236
18237     /**
18238      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18239      */
18240     refresh : function(){
18241         //Roo.log('refresh');
18242         var t = this.tpl;
18243         
18244         // if we are using something like 'domtemplate', then
18245         // the what gets used is:
18246         // t.applySubtemplate(NAME, data, wrapping data..)
18247         // the outer template then get' applied with
18248         //     the store 'extra data'
18249         // and the body get's added to the
18250         //      roo-name="data" node?
18251         //      <span class='roo-tpl-{name}'></span> ?????
18252         
18253         
18254         
18255         this.clearSelections();
18256         this.el.update("");
18257         var html = [];
18258         var records = this.store.getRange();
18259         if(records.length < 1) {
18260             
18261             // is this valid??  = should it render a template??
18262             
18263             this.el.update(this.emptyText);
18264             return;
18265         }
18266         var el = this.el;
18267         if (this.dataName) {
18268             this.el.update(t.apply(this.store.meta)); //????
18269             el = this.el.child('.roo-tpl-' + this.dataName);
18270         }
18271         
18272         for(var i = 0, len = records.length; i < len; i++){
18273             var data = this.prepareData(records[i].data, i, records[i]);
18274             this.fireEvent("preparedata", this, data, i, records[i]);
18275             
18276             var d = Roo.apply({}, data);
18277             
18278             if(this.tickable){
18279                 Roo.apply(d, {'roo-id' : Roo.id()});
18280                 
18281                 var _this = this;
18282             
18283                 Roo.each(this.parent.item, function(item){
18284                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18285                         return;
18286                     }
18287                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18288                 });
18289             }
18290             
18291             html[html.length] = Roo.util.Format.trim(
18292                 this.dataName ?
18293                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18294                     t.apply(d)
18295             );
18296         }
18297         
18298         
18299         
18300         el.update(html.join(""));
18301         this.nodes = el.dom.childNodes;
18302         this.updateIndexes(0);
18303     },
18304     
18305
18306     /**
18307      * Function to override to reformat the data that is sent to
18308      * the template for each node.
18309      * DEPRICATED - use the preparedata event handler.
18310      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18311      * a JSON object for an UpdateManager bound view).
18312      */
18313     prepareData : function(data, index, record)
18314     {
18315         this.fireEvent("preparedata", this, data, index, record);
18316         return data;
18317     },
18318
18319     onUpdate : function(ds, record){
18320         // Roo.log('on update');   
18321         this.clearSelections();
18322         var index = this.store.indexOf(record);
18323         var n = this.nodes[index];
18324         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18325         n.parentNode.removeChild(n);
18326         this.updateIndexes(index, index);
18327     },
18328
18329     
18330     
18331 // --------- FIXME     
18332     onAdd : function(ds, records, index)
18333     {
18334         //Roo.log(['on Add', ds, records, index] );        
18335         this.clearSelections();
18336         if(this.nodes.length == 0){
18337             this.refresh();
18338             return;
18339         }
18340         var n = this.nodes[index];
18341         for(var i = 0, len = records.length; i < len; i++){
18342             var d = this.prepareData(records[i].data, i, records[i]);
18343             if(n){
18344                 this.tpl.insertBefore(n, d);
18345             }else{
18346                 
18347                 this.tpl.append(this.el, d);
18348             }
18349         }
18350         this.updateIndexes(index);
18351     },
18352
18353     onRemove : function(ds, record, index){
18354        // Roo.log('onRemove');
18355         this.clearSelections();
18356         var el = this.dataName  ?
18357             this.el.child('.roo-tpl-' + this.dataName) :
18358             this.el; 
18359         
18360         el.dom.removeChild(this.nodes[index]);
18361         this.updateIndexes(index);
18362     },
18363
18364     /**
18365      * Refresh an individual node.
18366      * @param {Number} index
18367      */
18368     refreshNode : function(index){
18369         this.onUpdate(this.store, this.store.getAt(index));
18370     },
18371
18372     updateIndexes : function(startIndex, endIndex){
18373         var ns = this.nodes;
18374         startIndex = startIndex || 0;
18375         endIndex = endIndex || ns.length - 1;
18376         for(var i = startIndex; i <= endIndex; i++){
18377             ns[i].nodeIndex = i;
18378         }
18379     },
18380
18381     /**
18382      * Changes the data store this view uses and refresh the view.
18383      * @param {Store} store
18384      */
18385     setStore : function(store, initial){
18386         if(!initial && this.store){
18387             this.store.un("datachanged", this.refresh);
18388             this.store.un("add", this.onAdd);
18389             this.store.un("remove", this.onRemove);
18390             this.store.un("update", this.onUpdate);
18391             this.store.un("clear", this.refresh);
18392             this.store.un("beforeload", this.onBeforeLoad);
18393             this.store.un("load", this.onLoad);
18394             this.store.un("loadexception", this.onLoad);
18395         }
18396         if(store){
18397           
18398             store.on("datachanged", this.refresh, this);
18399             store.on("add", this.onAdd, this);
18400             store.on("remove", this.onRemove, this);
18401             store.on("update", this.onUpdate, this);
18402             store.on("clear", this.refresh, this);
18403             store.on("beforeload", this.onBeforeLoad, this);
18404             store.on("load", this.onLoad, this);
18405             store.on("loadexception", this.onLoad, this);
18406         }
18407         
18408         if(store){
18409             this.refresh();
18410         }
18411     },
18412     /**
18413      * onbeforeLoad - masks the loading area.
18414      *
18415      */
18416     onBeforeLoad : function(store,opts)
18417     {
18418          //Roo.log('onBeforeLoad');   
18419         if (!opts.add) {
18420             this.el.update("");
18421         }
18422         this.el.mask(this.mask ? this.mask : "Loading" ); 
18423     },
18424     onLoad : function ()
18425     {
18426         this.el.unmask();
18427     },
18428     
18429
18430     /**
18431      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18432      * @param {HTMLElement} node
18433      * @return {HTMLElement} The template node
18434      */
18435     findItemFromChild : function(node){
18436         var el = this.dataName  ?
18437             this.el.child('.roo-tpl-' + this.dataName,true) :
18438             this.el.dom; 
18439         
18440         if(!node || node.parentNode == el){
18441                     return node;
18442             }
18443             var p = node.parentNode;
18444             while(p && p != el){
18445             if(p.parentNode == el){
18446                 return p;
18447             }
18448             p = p.parentNode;
18449         }
18450             return null;
18451     },
18452
18453     /** @ignore */
18454     onClick : function(e){
18455         var item = this.findItemFromChild(e.getTarget());
18456         if(item){
18457             var index = this.indexOf(item);
18458             if(this.onItemClick(item, index, e) !== false){
18459                 this.fireEvent("click", this, index, item, e);
18460             }
18461         }else{
18462             this.clearSelections();
18463         }
18464     },
18465
18466     /** @ignore */
18467     onContextMenu : function(e){
18468         var item = this.findItemFromChild(e.getTarget());
18469         if(item){
18470             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18471         }
18472     },
18473
18474     /** @ignore */
18475     onDblClick : function(e){
18476         var item = this.findItemFromChild(e.getTarget());
18477         if(item){
18478             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18479         }
18480     },
18481
18482     onItemClick : function(item, index, e)
18483     {
18484         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18485             return false;
18486         }
18487         if (this.toggleSelect) {
18488             var m = this.isSelected(item) ? 'unselect' : 'select';
18489             //Roo.log(m);
18490             var _t = this;
18491             _t[m](item, true, false);
18492             return true;
18493         }
18494         if(this.multiSelect || this.singleSelect){
18495             if(this.multiSelect && e.shiftKey && this.lastSelection){
18496                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18497             }else{
18498                 this.select(item, this.multiSelect && e.ctrlKey);
18499                 this.lastSelection = item;
18500             }
18501             
18502             if(!this.tickable){
18503                 e.preventDefault();
18504             }
18505             
18506         }
18507         return true;
18508     },
18509
18510     /**
18511      * Get the number of selected nodes.
18512      * @return {Number}
18513      */
18514     getSelectionCount : function(){
18515         return this.selections.length;
18516     },
18517
18518     /**
18519      * Get the currently selected nodes.
18520      * @return {Array} An array of HTMLElements
18521      */
18522     getSelectedNodes : function(){
18523         return this.selections;
18524     },
18525
18526     /**
18527      * Get the indexes of the selected nodes.
18528      * @return {Array}
18529      */
18530     getSelectedIndexes : function(){
18531         var indexes = [], s = this.selections;
18532         for(var i = 0, len = s.length; i < len; i++){
18533             indexes.push(s[i].nodeIndex);
18534         }
18535         return indexes;
18536     },
18537
18538     /**
18539      * Clear all selections
18540      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18541      */
18542     clearSelections : function(suppressEvent){
18543         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18544             this.cmp.elements = this.selections;
18545             this.cmp.removeClass(this.selectedClass);
18546             this.selections = [];
18547             if(!suppressEvent){
18548                 this.fireEvent("selectionchange", this, this.selections);
18549             }
18550         }
18551     },
18552
18553     /**
18554      * Returns true if the passed node is selected
18555      * @param {HTMLElement/Number} node The node or node index
18556      * @return {Boolean}
18557      */
18558     isSelected : function(node){
18559         var s = this.selections;
18560         if(s.length < 1){
18561             return false;
18562         }
18563         node = this.getNode(node);
18564         return s.indexOf(node) !== -1;
18565     },
18566
18567     /**
18568      * Selects nodes.
18569      * @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
18570      * @param {Boolean} keepExisting (optional) true to keep existing selections
18571      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18572      */
18573     select : function(nodeInfo, keepExisting, suppressEvent){
18574         if(nodeInfo instanceof Array){
18575             if(!keepExisting){
18576                 this.clearSelections(true);
18577             }
18578             for(var i = 0, len = nodeInfo.length; i < len; i++){
18579                 this.select(nodeInfo[i], true, true);
18580             }
18581             return;
18582         } 
18583         var node = this.getNode(nodeInfo);
18584         if(!node || this.isSelected(node)){
18585             return; // already selected.
18586         }
18587         if(!keepExisting){
18588             this.clearSelections(true);
18589         }
18590         
18591         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18592             Roo.fly(node).addClass(this.selectedClass);
18593             this.selections.push(node);
18594             if(!suppressEvent){
18595                 this.fireEvent("selectionchange", this, this.selections);
18596             }
18597         }
18598         
18599         
18600     },
18601       /**
18602      * Unselects nodes.
18603      * @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
18604      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18605      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18606      */
18607     unselect : function(nodeInfo, keepExisting, suppressEvent)
18608     {
18609         if(nodeInfo instanceof Array){
18610             Roo.each(this.selections, function(s) {
18611                 this.unselect(s, nodeInfo);
18612             }, this);
18613             return;
18614         }
18615         var node = this.getNode(nodeInfo);
18616         if(!node || !this.isSelected(node)){
18617             //Roo.log("not selected");
18618             return; // not selected.
18619         }
18620         // fireevent???
18621         var ns = [];
18622         Roo.each(this.selections, function(s) {
18623             if (s == node ) {
18624                 Roo.fly(node).removeClass(this.selectedClass);
18625
18626                 return;
18627             }
18628             ns.push(s);
18629         },this);
18630         
18631         this.selections= ns;
18632         this.fireEvent("selectionchange", this, this.selections);
18633     },
18634
18635     /**
18636      * Gets a template node.
18637      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18638      * @return {HTMLElement} The node or null if it wasn't found
18639      */
18640     getNode : function(nodeInfo){
18641         if(typeof nodeInfo == "string"){
18642             return document.getElementById(nodeInfo);
18643         }else if(typeof nodeInfo == "number"){
18644             return this.nodes[nodeInfo];
18645         }
18646         return nodeInfo;
18647     },
18648
18649     /**
18650      * Gets a range template nodes.
18651      * @param {Number} startIndex
18652      * @param {Number} endIndex
18653      * @return {Array} An array of nodes
18654      */
18655     getNodes : function(start, end){
18656         var ns = this.nodes;
18657         start = start || 0;
18658         end = typeof end == "undefined" ? ns.length - 1 : end;
18659         var nodes = [];
18660         if(start <= end){
18661             for(var i = start; i <= end; i++){
18662                 nodes.push(ns[i]);
18663             }
18664         } else{
18665             for(var i = start; i >= end; i--){
18666                 nodes.push(ns[i]);
18667             }
18668         }
18669         return nodes;
18670     },
18671
18672     /**
18673      * Finds the index of the passed node
18674      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18675      * @return {Number} The index of the node or -1
18676      */
18677     indexOf : function(node){
18678         node = this.getNode(node);
18679         if(typeof node.nodeIndex == "number"){
18680             return node.nodeIndex;
18681         }
18682         var ns = this.nodes;
18683         for(var i = 0, len = ns.length; i < len; i++){
18684             if(ns[i] == node){
18685                 return i;
18686             }
18687         }
18688         return -1;
18689     }
18690 });
18691 /*
18692  * - LGPL
18693  *
18694  * based on jquery fullcalendar
18695  * 
18696  */
18697
18698 Roo.bootstrap = Roo.bootstrap || {};
18699 /**
18700  * @class Roo.bootstrap.Calendar
18701  * @extends Roo.bootstrap.Component
18702  * Bootstrap Calendar class
18703  * @cfg {Boolean} loadMask (true|false) default false
18704  * @cfg {Object} header generate the user specific header of the calendar, default false
18705
18706  * @constructor
18707  * Create a new Container
18708  * @param {Object} config The config object
18709  */
18710
18711
18712
18713 Roo.bootstrap.Calendar = function(config){
18714     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18715      this.addEvents({
18716         /**
18717              * @event select
18718              * Fires when a date is selected
18719              * @param {DatePicker} this
18720              * @param {Date} date The selected date
18721              */
18722         'select': true,
18723         /**
18724              * @event monthchange
18725              * Fires when the displayed month changes 
18726              * @param {DatePicker} this
18727              * @param {Date} date The selected month
18728              */
18729         'monthchange': true,
18730         /**
18731              * @event evententer
18732              * Fires when mouse over an event
18733              * @param {Calendar} this
18734              * @param {event} Event
18735              */
18736         'evententer': true,
18737         /**
18738              * @event eventleave
18739              * Fires when the mouse leaves an
18740              * @param {Calendar} this
18741              * @param {event}
18742              */
18743         'eventleave': true,
18744         /**
18745              * @event eventclick
18746              * Fires when the mouse click an
18747              * @param {Calendar} this
18748              * @param {event}
18749              */
18750         'eventclick': true
18751         
18752     });
18753
18754 };
18755
18756 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18757     
18758      /**
18759      * @cfg {Number} startDay
18760      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18761      */
18762     startDay : 0,
18763     
18764     loadMask : false,
18765     
18766     header : false,
18767       
18768     getAutoCreate : function(){
18769         
18770         
18771         var fc_button = function(name, corner, style, content ) {
18772             return Roo.apply({},{
18773                 tag : 'span',
18774                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18775                          (corner.length ?
18776                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18777                             ''
18778                         ),
18779                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18780                 unselectable: 'on'
18781             });
18782         };
18783         
18784         var header = {};
18785         
18786         if(!this.header){
18787             header = {
18788                 tag : 'table',
18789                 cls : 'fc-header',
18790                 style : 'width:100%',
18791                 cn : [
18792                     {
18793                         tag: 'tr',
18794                         cn : [
18795                             {
18796                                 tag : 'td',
18797                                 cls : 'fc-header-left',
18798                                 cn : [
18799                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18800                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18801                                     { tag: 'span', cls: 'fc-header-space' },
18802                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18803
18804
18805                                 ]
18806                             },
18807
18808                             {
18809                                 tag : 'td',
18810                                 cls : 'fc-header-center',
18811                                 cn : [
18812                                     {
18813                                         tag: 'span',
18814                                         cls: 'fc-header-title',
18815                                         cn : {
18816                                             tag: 'H2',
18817                                             html : 'month / year'
18818                                         }
18819                                     }
18820
18821                                 ]
18822                             },
18823                             {
18824                                 tag : 'td',
18825                                 cls : 'fc-header-right',
18826                                 cn : [
18827                               /*      fc_button('month', 'left', '', 'month' ),
18828                                     fc_button('week', '', '', 'week' ),
18829                                     fc_button('day', 'right', '', 'day' )
18830                                 */    
18831
18832                                 ]
18833                             }
18834
18835                         ]
18836                     }
18837                 ]
18838             };
18839         }
18840         
18841         header = this.header;
18842         
18843        
18844         var cal_heads = function() {
18845             var ret = [];
18846             // fixme - handle this.
18847             
18848             for (var i =0; i < Date.dayNames.length; i++) {
18849                 var d = Date.dayNames[i];
18850                 ret.push({
18851                     tag: 'th',
18852                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18853                     html : d.substring(0,3)
18854                 });
18855                 
18856             }
18857             ret[0].cls += ' fc-first';
18858             ret[6].cls += ' fc-last';
18859             return ret;
18860         };
18861         var cal_cell = function(n) {
18862             return  {
18863                 tag: 'td',
18864                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18865                 cn : [
18866                     {
18867                         cn : [
18868                             {
18869                                 cls: 'fc-day-number',
18870                                 html: 'D'
18871                             },
18872                             {
18873                                 cls: 'fc-day-content',
18874                              
18875                                 cn : [
18876                                      {
18877                                         style: 'position: relative;' // height: 17px;
18878                                     }
18879                                 ]
18880                             }
18881                             
18882                             
18883                         ]
18884                     }
18885                 ]
18886                 
18887             }
18888         };
18889         var cal_rows = function() {
18890             
18891             var ret = [];
18892             for (var r = 0; r < 6; r++) {
18893                 var row= {
18894                     tag : 'tr',
18895                     cls : 'fc-week',
18896                     cn : []
18897                 };
18898                 
18899                 for (var i =0; i < Date.dayNames.length; i++) {
18900                     var d = Date.dayNames[i];
18901                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18902
18903                 }
18904                 row.cn[0].cls+=' fc-first';
18905                 row.cn[0].cn[0].style = 'min-height:90px';
18906                 row.cn[6].cls+=' fc-last';
18907                 ret.push(row);
18908                 
18909             }
18910             ret[0].cls += ' fc-first';
18911             ret[4].cls += ' fc-prev-last';
18912             ret[5].cls += ' fc-last';
18913             return ret;
18914             
18915         };
18916         
18917         var cal_table = {
18918             tag: 'table',
18919             cls: 'fc-border-separate',
18920             style : 'width:100%',
18921             cellspacing  : 0,
18922             cn : [
18923                 { 
18924                     tag: 'thead',
18925                     cn : [
18926                         { 
18927                             tag: 'tr',
18928                             cls : 'fc-first fc-last',
18929                             cn : cal_heads()
18930                         }
18931                     ]
18932                 },
18933                 { 
18934                     tag: 'tbody',
18935                     cn : cal_rows()
18936                 }
18937                   
18938             ]
18939         };
18940          
18941          var cfg = {
18942             cls : 'fc fc-ltr',
18943             cn : [
18944                 header,
18945                 {
18946                     cls : 'fc-content',
18947                     style : "position: relative;",
18948                     cn : [
18949                         {
18950                             cls : 'fc-view fc-view-month fc-grid',
18951                             style : 'position: relative',
18952                             unselectable : 'on',
18953                             cn : [
18954                                 {
18955                                     cls : 'fc-event-container',
18956                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18957                                 },
18958                                 cal_table
18959                             ]
18960                         }
18961                     ]
18962     
18963                 }
18964            ] 
18965             
18966         };
18967         
18968          
18969         
18970         return cfg;
18971     },
18972     
18973     
18974     initEvents : function()
18975     {
18976         if(!this.store){
18977             throw "can not find store for calendar";
18978         }
18979         
18980         var mark = {
18981             tag: "div",
18982             cls:"x-dlg-mask",
18983             style: "text-align:center",
18984             cn: [
18985                 {
18986                     tag: "div",
18987                     style: "background-color:white;width:50%;margin:250 auto",
18988                     cn: [
18989                         {
18990                             tag: "img",
18991                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18992                         },
18993                         {
18994                             tag: "span",
18995                             html: "Loading"
18996                         }
18997                         
18998                     ]
18999                 }
19000             ]
19001         };
19002         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19003         
19004         var size = this.el.select('.fc-content', true).first().getSize();
19005         this.maskEl.setSize(size.width, size.height);
19006         this.maskEl.enableDisplayMode("block");
19007         if(!this.loadMask){
19008             this.maskEl.hide();
19009         }
19010         
19011         this.store = Roo.factory(this.store, Roo.data);
19012         this.store.on('load', this.onLoad, this);
19013         this.store.on('beforeload', this.onBeforeLoad, this);
19014         
19015         this.resize();
19016         
19017         this.cells = this.el.select('.fc-day',true);
19018         //Roo.log(this.cells);
19019         this.textNodes = this.el.query('.fc-day-number');
19020         this.cells.addClassOnOver('fc-state-hover');
19021         
19022         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19023         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19024         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19025         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19026         
19027         this.on('monthchange', this.onMonthChange, this);
19028         
19029         this.update(new Date().clearTime());
19030     },
19031     
19032     resize : function() {
19033         var sz  = this.el.getSize();
19034         
19035         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19036         this.el.select('.fc-day-content div',true).setHeight(34);
19037     },
19038     
19039     
19040     // private
19041     showPrevMonth : function(e){
19042         this.update(this.activeDate.add("mo", -1));
19043     },
19044     showToday : function(e){
19045         this.update(new Date().clearTime());
19046     },
19047     // private
19048     showNextMonth : function(e){
19049         this.update(this.activeDate.add("mo", 1));
19050     },
19051
19052     // private
19053     showPrevYear : function(){
19054         this.update(this.activeDate.add("y", -1));
19055     },
19056
19057     // private
19058     showNextYear : function(){
19059         this.update(this.activeDate.add("y", 1));
19060     },
19061
19062     
19063    // private
19064     update : function(date)
19065     {
19066         var vd = this.activeDate;
19067         this.activeDate = date;
19068 //        if(vd && this.el){
19069 //            var t = date.getTime();
19070 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19071 //                Roo.log('using add remove');
19072 //                
19073 //                this.fireEvent('monthchange', this, date);
19074 //                
19075 //                this.cells.removeClass("fc-state-highlight");
19076 //                this.cells.each(function(c){
19077 //                   if(c.dateValue == t){
19078 //                       c.addClass("fc-state-highlight");
19079 //                       setTimeout(function(){
19080 //                            try{c.dom.firstChild.focus();}catch(e){}
19081 //                       }, 50);
19082 //                       return false;
19083 //                   }
19084 //                   return true;
19085 //                });
19086 //                return;
19087 //            }
19088 //        }
19089         
19090         var days = date.getDaysInMonth();
19091         
19092         var firstOfMonth = date.getFirstDateOfMonth();
19093         var startingPos = firstOfMonth.getDay()-this.startDay;
19094         
19095         if(startingPos < this.startDay){
19096             startingPos += 7;
19097         }
19098         
19099         var pm = date.add(Date.MONTH, -1);
19100         var prevStart = pm.getDaysInMonth()-startingPos;
19101 //        
19102         this.cells = this.el.select('.fc-day',true);
19103         this.textNodes = this.el.query('.fc-day-number');
19104         this.cells.addClassOnOver('fc-state-hover');
19105         
19106         var cells = this.cells.elements;
19107         var textEls = this.textNodes;
19108         
19109         Roo.each(cells, function(cell){
19110             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19111         });
19112         
19113         days += startingPos;
19114
19115         // convert everything to numbers so it's fast
19116         var day = 86400000;
19117         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19118         //Roo.log(d);
19119         //Roo.log(pm);
19120         //Roo.log(prevStart);
19121         
19122         var today = new Date().clearTime().getTime();
19123         var sel = date.clearTime().getTime();
19124         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19125         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19126         var ddMatch = this.disabledDatesRE;
19127         var ddText = this.disabledDatesText;
19128         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19129         var ddaysText = this.disabledDaysText;
19130         var format = this.format;
19131         
19132         var setCellClass = function(cal, cell){
19133             cell.row = 0;
19134             cell.events = [];
19135             cell.more = [];
19136             //Roo.log('set Cell Class');
19137             cell.title = "";
19138             var t = d.getTime();
19139             
19140             //Roo.log(d);
19141             
19142             cell.dateValue = t;
19143             if(t == today){
19144                 cell.className += " fc-today";
19145                 cell.className += " fc-state-highlight";
19146                 cell.title = cal.todayText;
19147             }
19148             if(t == sel){
19149                 // disable highlight in other month..
19150                 //cell.className += " fc-state-highlight";
19151                 
19152             }
19153             // disabling
19154             if(t < min) {
19155                 cell.className = " fc-state-disabled";
19156                 cell.title = cal.minText;
19157                 return;
19158             }
19159             if(t > max) {
19160                 cell.className = " fc-state-disabled";
19161                 cell.title = cal.maxText;
19162                 return;
19163             }
19164             if(ddays){
19165                 if(ddays.indexOf(d.getDay()) != -1){
19166                     cell.title = ddaysText;
19167                     cell.className = " fc-state-disabled";
19168                 }
19169             }
19170             if(ddMatch && format){
19171                 var fvalue = d.dateFormat(format);
19172                 if(ddMatch.test(fvalue)){
19173                     cell.title = ddText.replace("%0", fvalue);
19174                     cell.className = " fc-state-disabled";
19175                 }
19176             }
19177             
19178             if (!cell.initialClassName) {
19179                 cell.initialClassName = cell.dom.className;
19180             }
19181             
19182             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19183         };
19184
19185         var i = 0;
19186         
19187         for(; i < startingPos; i++) {
19188             textEls[i].innerHTML = (++prevStart);
19189             d.setDate(d.getDate()+1);
19190             
19191             cells[i].className = "fc-past fc-other-month";
19192             setCellClass(this, cells[i]);
19193         }
19194         
19195         var intDay = 0;
19196         
19197         for(; i < days; i++){
19198             intDay = i - startingPos + 1;
19199             textEls[i].innerHTML = (intDay);
19200             d.setDate(d.getDate()+1);
19201             
19202             cells[i].className = ''; // "x-date-active";
19203             setCellClass(this, cells[i]);
19204         }
19205         var extraDays = 0;
19206         
19207         for(; i < 42; i++) {
19208             textEls[i].innerHTML = (++extraDays);
19209             d.setDate(d.getDate()+1);
19210             
19211             cells[i].className = "fc-future fc-other-month";
19212             setCellClass(this, cells[i]);
19213         }
19214         
19215         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19216         
19217         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19218         
19219         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19220         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19221         
19222         if(totalRows != 6){
19223             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19224             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19225         }
19226         
19227         this.fireEvent('monthchange', this, date);
19228         
19229         
19230         /*
19231         if(!this.internalRender){
19232             var main = this.el.dom.firstChild;
19233             var w = main.offsetWidth;
19234             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19235             Roo.fly(main).setWidth(w);
19236             this.internalRender = true;
19237             // opera does not respect the auto grow header center column
19238             // then, after it gets a width opera refuses to recalculate
19239             // without a second pass
19240             if(Roo.isOpera && !this.secondPass){
19241                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19242                 this.secondPass = true;
19243                 this.update.defer(10, this, [date]);
19244             }
19245         }
19246         */
19247         
19248     },
19249     
19250     findCell : function(dt) {
19251         dt = dt.clearTime().getTime();
19252         var ret = false;
19253         this.cells.each(function(c){
19254             //Roo.log("check " +c.dateValue + '?=' + dt);
19255             if(c.dateValue == dt){
19256                 ret = c;
19257                 return false;
19258             }
19259             return true;
19260         });
19261         
19262         return ret;
19263     },
19264     
19265     findCells : function(ev) {
19266         var s = ev.start.clone().clearTime().getTime();
19267        // Roo.log(s);
19268         var e= ev.end.clone().clearTime().getTime();
19269        // Roo.log(e);
19270         var ret = [];
19271         this.cells.each(function(c){
19272              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19273             
19274             if(c.dateValue > e){
19275                 return ;
19276             }
19277             if(c.dateValue < s){
19278                 return ;
19279             }
19280             ret.push(c);
19281         });
19282         
19283         return ret;    
19284     },
19285     
19286 //    findBestRow: function(cells)
19287 //    {
19288 //        var ret = 0;
19289 //        
19290 //        for (var i =0 ; i < cells.length;i++) {
19291 //            ret  = Math.max(cells[i].rows || 0,ret);
19292 //        }
19293 //        return ret;
19294 //        
19295 //    },
19296     
19297     
19298     addItem : function(ev)
19299     {
19300         // look for vertical location slot in
19301         var cells = this.findCells(ev);
19302         
19303 //        ev.row = this.findBestRow(cells);
19304         
19305         // work out the location.
19306         
19307         var crow = false;
19308         var rows = [];
19309         for(var i =0; i < cells.length; i++) {
19310             
19311             cells[i].row = cells[0].row;
19312             
19313             if(i == 0){
19314                 cells[i].row = cells[i].row + 1;
19315             }
19316             
19317             if (!crow) {
19318                 crow = {
19319                     start : cells[i],
19320                     end :  cells[i]
19321                 };
19322                 continue;
19323             }
19324             if (crow.start.getY() == cells[i].getY()) {
19325                 // on same row.
19326                 crow.end = cells[i];
19327                 continue;
19328             }
19329             // different row.
19330             rows.push(crow);
19331             crow = {
19332                 start: cells[i],
19333                 end : cells[i]
19334             };
19335             
19336         }
19337         
19338         rows.push(crow);
19339         ev.els = [];
19340         ev.rows = rows;
19341         ev.cells = cells;
19342         
19343         cells[0].events.push(ev);
19344         
19345         this.calevents.push(ev);
19346     },
19347     
19348     clearEvents: function() {
19349         
19350         if(!this.calevents){
19351             return;
19352         }
19353         
19354         Roo.each(this.cells.elements, function(c){
19355             c.row = 0;
19356             c.events = [];
19357             c.more = [];
19358         });
19359         
19360         Roo.each(this.calevents, function(e) {
19361             Roo.each(e.els, function(el) {
19362                 el.un('mouseenter' ,this.onEventEnter, this);
19363                 el.un('mouseleave' ,this.onEventLeave, this);
19364                 el.remove();
19365             },this);
19366         },this);
19367         
19368         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19369             e.remove();
19370         });
19371         
19372     },
19373     
19374     renderEvents: function()
19375     {   
19376         var _this = this;
19377         
19378         this.cells.each(function(c) {
19379             
19380             if(c.row < 5){
19381                 return;
19382             }
19383             
19384             var ev = c.events;
19385             
19386             var r = 4;
19387             if(c.row != c.events.length){
19388                 r = 4 - (4 - (c.row - c.events.length));
19389             }
19390             
19391             c.events = ev.slice(0, r);
19392             c.more = ev.slice(r);
19393             
19394             if(c.more.length && c.more.length == 1){
19395                 c.events.push(c.more.pop());
19396             }
19397             
19398             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19399             
19400         });
19401             
19402         this.cells.each(function(c) {
19403             
19404             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19405             
19406             
19407             for (var e = 0; e < c.events.length; e++){
19408                 var ev = c.events[e];
19409                 var rows = ev.rows;
19410                 
19411                 for(var i = 0; i < rows.length; i++) {
19412                 
19413                     // how many rows should it span..
19414
19415                     var  cfg = {
19416                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19417                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19418
19419                         unselectable : "on",
19420                         cn : [
19421                             {
19422                                 cls: 'fc-event-inner',
19423                                 cn : [
19424     //                                {
19425     //                                  tag:'span',
19426     //                                  cls: 'fc-event-time',
19427     //                                  html : cells.length > 1 ? '' : ev.time
19428     //                                },
19429                                     {
19430                                       tag:'span',
19431                                       cls: 'fc-event-title',
19432                                       html : String.format('{0}', ev.title)
19433                                     }
19434
19435
19436                                 ]
19437                             },
19438                             {
19439                                 cls: 'ui-resizable-handle ui-resizable-e',
19440                                 html : '&nbsp;&nbsp;&nbsp'
19441                             }
19442
19443                         ]
19444                     };
19445
19446                     if (i == 0) {
19447                         cfg.cls += ' fc-event-start';
19448                     }
19449                     if ((i+1) == rows.length) {
19450                         cfg.cls += ' fc-event-end';
19451                     }
19452
19453                     var ctr = _this.el.select('.fc-event-container',true).first();
19454                     var cg = ctr.createChild(cfg);
19455
19456                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19457                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19458
19459                     var r = (c.more.length) ? 1 : 0;
19460                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19461                     cg.setWidth(ebox.right - sbox.x -2);
19462
19463                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19464                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19465                     cg.on('click', _this.onEventClick, _this, ev);
19466
19467                     ev.els.push(cg);
19468                     
19469                 }
19470                 
19471             }
19472             
19473             
19474             if(c.more.length){
19475                 var  cfg = {
19476                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19477                     style : 'position: absolute',
19478                     unselectable : "on",
19479                     cn : [
19480                         {
19481                             cls: 'fc-event-inner',
19482                             cn : [
19483                                 {
19484                                   tag:'span',
19485                                   cls: 'fc-event-title',
19486                                   html : 'More'
19487                                 }
19488
19489
19490                             ]
19491                         },
19492                         {
19493                             cls: 'ui-resizable-handle ui-resizable-e',
19494                             html : '&nbsp;&nbsp;&nbsp'
19495                         }
19496
19497                     ]
19498                 };
19499
19500                 var ctr = _this.el.select('.fc-event-container',true).first();
19501                 var cg = ctr.createChild(cfg);
19502
19503                 var sbox = c.select('.fc-day-content',true).first().getBox();
19504                 var ebox = c.select('.fc-day-content',true).first().getBox();
19505                 //Roo.log(cg);
19506                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19507                 cg.setWidth(ebox.right - sbox.x -2);
19508
19509                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19510                 
19511             }
19512             
19513         });
19514         
19515         
19516         
19517     },
19518     
19519     onEventEnter: function (e, el,event,d) {
19520         this.fireEvent('evententer', this, el, event);
19521     },
19522     
19523     onEventLeave: function (e, el,event,d) {
19524         this.fireEvent('eventleave', this, el, event);
19525     },
19526     
19527     onEventClick: function (e, el,event,d) {
19528         this.fireEvent('eventclick', this, el, event);
19529     },
19530     
19531     onMonthChange: function () {
19532         this.store.load();
19533     },
19534     
19535     onMoreEventClick: function(e, el, more)
19536     {
19537         var _this = this;
19538         
19539         this.calpopover.placement = 'right';
19540         this.calpopover.setTitle('More');
19541         
19542         this.calpopover.setContent('');
19543         
19544         var ctr = this.calpopover.el.select('.popover-content', true).first();
19545         
19546         Roo.each(more, function(m){
19547             var cfg = {
19548                 cls : 'fc-event-hori fc-event-draggable',
19549                 html : m.title
19550             };
19551             var cg = ctr.createChild(cfg);
19552             
19553             cg.on('click', _this.onEventClick, _this, m);
19554         });
19555         
19556         this.calpopover.show(el);
19557         
19558         
19559     },
19560     
19561     onLoad: function () 
19562     {   
19563         this.calevents = [];
19564         var cal = this;
19565         
19566         if(this.store.getCount() > 0){
19567             this.store.data.each(function(d){
19568                cal.addItem({
19569                     id : d.data.id,
19570                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19571                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19572                     time : d.data.start_time,
19573                     title : d.data.title,
19574                     description : d.data.description,
19575                     venue : d.data.venue
19576                 });
19577             });
19578         }
19579         
19580         this.renderEvents();
19581         
19582         if(this.calevents.length && this.loadMask){
19583             this.maskEl.hide();
19584         }
19585     },
19586     
19587     onBeforeLoad: function()
19588     {
19589         this.clearEvents();
19590         if(this.loadMask){
19591             this.maskEl.show();
19592         }
19593     }
19594 });
19595
19596  
19597  /*
19598  * - LGPL
19599  *
19600  * element
19601  * 
19602  */
19603
19604 /**
19605  * @class Roo.bootstrap.Popover
19606  * @extends Roo.bootstrap.Component
19607  * Bootstrap Popover class
19608  * @cfg {String} html contents of the popover   (or false to use children..)
19609  * @cfg {String} title of popover (or false to hide)
19610  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19611  * @cfg {String} trigger click || hover (or false to trigger manually)
19612  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19613  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19614  *      - if false and it has a 'parent' then it will be automatically added to that element
19615  *      - if string - Roo.get  will be called 
19616  * @cfg {Number} delay - delay before showing
19617  
19618  * @constructor
19619  * Create a new Popover
19620  * @param {Object} config The config object
19621  */
19622
19623 Roo.bootstrap.Popover = function(config){
19624     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19625     
19626     this.addEvents({
19627         // raw events
19628          /**
19629          * @event show
19630          * After the popover show
19631          * 
19632          * @param {Roo.bootstrap.Popover} this
19633          */
19634         "show" : true,
19635         /**
19636          * @event hide
19637          * After the popover hide
19638          * 
19639          * @param {Roo.bootstrap.Popover} this
19640          */
19641         "hide" : true
19642     });
19643 };
19644
19645 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19646     
19647     title: false,
19648     html: false,
19649     
19650     placement : 'right',
19651     trigger : 'hover', // hover
19652     modal : false,
19653     delay : 0,
19654     
19655     over: false,
19656     
19657     can_build_overlaid : false,
19658     
19659     maskEl : false, // the mask element
19660     headerEl : false,
19661     contentEl : false,
19662     
19663     
19664     getChildContainer : function()
19665     {
19666         return this.contentEl;
19667         
19668     },
19669     getPopoverHeader : function()
19670     {
19671         this.title = true; // flag not to hide it..
19672         this.headerEl.addClass('p-0');
19673         return this.headerEl
19674     },
19675     
19676     
19677     getAutoCreate : function(){
19678          
19679         var cfg = {
19680            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19681            style: 'display:block',
19682            cn : [
19683                 {
19684                     cls : 'arrow'
19685                 },
19686                 {
19687                     cls : 'popover-inner ',
19688                     cn : [
19689                         {
19690                             tag: 'h3',
19691                             cls: 'popover-title popover-header',
19692                             html : this.title === false ? '' : this.title
19693                         },
19694                         {
19695                             cls : 'popover-content popover-body '  + (this.cls || ''),
19696                             html : this.html || ''
19697                         }
19698                     ]
19699                     
19700                 }
19701            ]
19702         };
19703         
19704         return cfg;
19705     },
19706     /**
19707      * @param {string} the title
19708      */
19709     setTitle: function(str)
19710     {
19711         this.title = str;
19712         if (this.el) {
19713             this.headerEl.dom.innerHTML = str;
19714         }
19715         
19716     },
19717     /**
19718      * @param {string} the body content
19719      */
19720     setContent: function(str)
19721     {
19722         this.html = str;
19723         if (this.contentEl) {
19724             this.contentEl.dom.innerHTML = str;
19725         }
19726         
19727     },
19728     // as it get's added to the bottom of the page.
19729     onRender : function(ct, position)
19730     {
19731         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19732         
19733         
19734         
19735         if(!this.el){
19736             var cfg = Roo.apply({},  this.getAutoCreate());
19737             cfg.id = Roo.id();
19738             
19739             if (this.cls) {
19740                 cfg.cls += ' ' + this.cls;
19741             }
19742             if (this.style) {
19743                 cfg.style = this.style;
19744             }
19745             //Roo.log("adding to ");
19746             this.el = Roo.get(document.body).createChild(cfg, position);
19747 //            Roo.log(this.el);
19748         }
19749         
19750         this.contentEl = this.el.select('.popover-content',true).first();
19751         this.headerEl =  this.el.select('.popover-title',true).first();
19752         
19753         var nitems = [];
19754         if(typeof(this.items) != 'undefined'){
19755             var items = this.items;
19756             delete this.items;
19757
19758             for(var i =0;i < items.length;i++) {
19759                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19760             }
19761         }
19762
19763         this.items = nitems;
19764         
19765         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19766         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19767         
19768         
19769         
19770         this.initEvents();
19771     },
19772     
19773     resizeMask : function()
19774     {
19775         this.maskEl.setSize(
19776             Roo.lib.Dom.getViewWidth(true),
19777             Roo.lib.Dom.getViewHeight(true)
19778         );
19779     },
19780     
19781     initEvents : function()
19782     {
19783         
19784         if (!this.modal) { 
19785             Roo.bootstrap.Popover.register(this);
19786         }
19787          
19788         
19789         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19790         this.el.enableDisplayMode('block');
19791         this.el.hide();
19792         if (this.over === false && !this.parent()) {
19793             return; 
19794         }
19795         if (this.triggers === false) {
19796             return;
19797         }
19798          
19799         // support parent
19800         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19801         var triggers = this.trigger ? this.trigger.split(' ') : [];
19802         Roo.each(triggers, function(trigger) {
19803         
19804             if (trigger == 'click') {
19805                 on_el.on('click', this.toggle, this);
19806             } else if (trigger != 'manual') {
19807                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19808                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19809       
19810                 on_el.on(eventIn  ,this.enter, this);
19811                 on_el.on(eventOut, this.leave, this);
19812             }
19813         }, this);
19814         
19815     },
19816     
19817     
19818     // private
19819     timeout : null,
19820     hoverState : null,
19821     
19822     toggle : function () {
19823         this.hoverState == 'in' ? this.leave() : this.enter();
19824     },
19825     
19826     enter : function () {
19827         
19828         clearTimeout(this.timeout);
19829     
19830         this.hoverState = 'in';
19831     
19832         if (!this.delay || !this.delay.show) {
19833             this.show();
19834             return;
19835         }
19836         var _t = this;
19837         this.timeout = setTimeout(function () {
19838             if (_t.hoverState == 'in') {
19839                 _t.show();
19840             }
19841         }, this.delay.show)
19842     },
19843     
19844     leave : function() {
19845         clearTimeout(this.timeout);
19846     
19847         this.hoverState = 'out';
19848     
19849         if (!this.delay || !this.delay.hide) {
19850             this.hide();
19851             return;
19852         }
19853         var _t = this;
19854         this.timeout = setTimeout(function () {
19855             if (_t.hoverState == 'out') {
19856                 _t.hide();
19857             }
19858         }, this.delay.hide)
19859     },
19860     /**
19861      * Show the popover
19862      * @param {Roo.Element|string|false} - element to align and point to.
19863      */
19864     show : function (on_el)
19865     {
19866         
19867         on_el = on_el || false; // default to false
19868         if (!on_el) {
19869             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19870                 on_el = this.parent().el;
19871             } else if (this.over) {
19872                 Roo.get(this.over);
19873             }
19874             
19875         }
19876         
19877         if (!this.el) {
19878             this.render(document.body);
19879         }
19880         
19881         
19882         this.el.removeClass([
19883             'fade','top','bottom', 'left', 'right','in',
19884             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19885         ]);
19886         
19887         if (this.title === false) {
19888             this.headerEl.hide();
19889         }
19890         
19891         
19892         var placement = typeof this.placement == 'function' ?
19893             this.placement.call(this, this.el, on_el) :
19894             this.placement;
19895             
19896         /*
19897         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19898         
19899         // I think  'auto right' - but 
19900         
19901         var autoPlace = autoToken.test(placement);
19902         if (autoPlace) {
19903             placement = placement.replace(autoToken, '') || 'top';
19904         }
19905         */
19906         
19907         
19908         this.el.show();
19909         this.el.dom.style.display='block';
19910         
19911         //this.el.appendTo(on_el);
19912         
19913         var p = this.getPosition();
19914         var box = this.el.getBox();
19915         
19916         
19917         var align = Roo.bootstrap.Popover.alignment[placement];
19918         this.el.addClass(align[2]);
19919
19920 //        Roo.log(align);
19921
19922         if (on_el) {
19923             this.el.alignTo(on_el, align[0],align[1]);
19924         } else {
19925             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19926             var es = this.el.getSize();
19927             var x = Roo.lib.Dom.getViewWidth()/2;
19928             var y = Roo.lib.Dom.getViewHeight()/2;
19929             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19930             
19931         }
19932
19933         
19934         //var arrow = this.el.select('.arrow',true).first();
19935         //arrow.set(align[2], 
19936         
19937         this.el.addClass('in');
19938         
19939         
19940         if (this.el.hasClass('fade')) {
19941             // fade it?
19942         }
19943         
19944         this.hoverState = 'in';
19945         
19946         if (this.modal) {
19947             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19948             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19949             this.maskEl.dom.style.display = 'block';
19950             this.maskEl.addClass('show');
19951         }
19952         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19953
19954         
19955         
19956         this.fireEvent('show', this);
19957         
19958     },
19959     hide : function()
19960     {
19961         this.el.setXY([0,0]);
19962         this.el.removeClass('in');
19963         this.el.hide();
19964         this.hoverState = null;
19965         this.maskEl.hide(); // always..
19966         this.fireEvent('hide', this);
19967     }
19968     
19969 });
19970
19971
19972 Roo.apply(Roo.bootstrap.Popover, {
19973
19974     alignment : {
19975         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19976         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19977         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19978         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19979     },
19980     
19981     zIndex : 20001,
19982
19983     clickHander : false,
19984     
19985
19986     onMouseDown : function(e)
19987     {
19988         if (!e.getTarget(".roo-popover")) {
19989             this.hideAll();
19990         }
19991          
19992     },
19993     
19994     popups : [],
19995     
19996     register : function(popup)
19997     {
19998         if (!Roo.bootstrap.Popover.clickHandler) {
19999             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20000         }
20001         // hide other popups.
20002         this.hideAll();
20003         this.popups.push(popup);
20004     },
20005     hideAll : function()
20006     {
20007         this.popups.forEach(function(p) {
20008             p.hide();
20009         });
20010     }
20011
20012 });/*
20013  * - LGPL
20014  *
20015  * Card header - holder for the card header elements.
20016  * 
20017  */
20018
20019 /**
20020  * @class Roo.bootstrap.PopoverNav
20021  * @extends Roo.bootstrap.NavGroup
20022  * Bootstrap Popover header navigation class
20023  * @constructor
20024  * Create a new Popover Header Navigation 
20025  * @param {Object} config The config object
20026  */
20027
20028 Roo.bootstrap.PopoverNav = function(config){
20029     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20030 };
20031
20032 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20033     
20034     
20035     container_method : 'getPopoverHeader' 
20036     
20037      
20038     
20039     
20040    
20041 });
20042
20043  
20044
20045  /*
20046  * - LGPL
20047  *
20048  * Progress
20049  * 
20050  */
20051
20052 /**
20053  * @class Roo.bootstrap.Progress
20054  * @extends Roo.bootstrap.Component
20055  * Bootstrap Progress class
20056  * @cfg {Boolean} striped striped of the progress bar
20057  * @cfg {Boolean} active animated of the progress bar
20058  * 
20059  * 
20060  * @constructor
20061  * Create a new Progress
20062  * @param {Object} config The config object
20063  */
20064
20065 Roo.bootstrap.Progress = function(config){
20066     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20067 };
20068
20069 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20070     
20071     striped : false,
20072     active: false,
20073     
20074     getAutoCreate : function(){
20075         var cfg = {
20076             tag: 'div',
20077             cls: 'progress'
20078         };
20079         
20080         
20081         if(this.striped){
20082             cfg.cls += ' progress-striped';
20083         }
20084       
20085         if(this.active){
20086             cfg.cls += ' active';
20087         }
20088         
20089         
20090         return cfg;
20091     }
20092    
20093 });
20094
20095  
20096
20097  /*
20098  * - LGPL
20099  *
20100  * ProgressBar
20101  * 
20102  */
20103
20104 /**
20105  * @class Roo.bootstrap.ProgressBar
20106  * @extends Roo.bootstrap.Component
20107  * Bootstrap ProgressBar class
20108  * @cfg {Number} aria_valuenow aria-value now
20109  * @cfg {Number} aria_valuemin aria-value min
20110  * @cfg {Number} aria_valuemax aria-value max
20111  * @cfg {String} label label for the progress bar
20112  * @cfg {String} panel (success | info | warning | danger )
20113  * @cfg {String} role role of the progress bar
20114  * @cfg {String} sr_only text
20115  * 
20116  * 
20117  * @constructor
20118  * Create a new ProgressBar
20119  * @param {Object} config The config object
20120  */
20121
20122 Roo.bootstrap.ProgressBar = function(config){
20123     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20124 };
20125
20126 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20127     
20128     aria_valuenow : 0,
20129     aria_valuemin : 0,
20130     aria_valuemax : 100,
20131     label : false,
20132     panel : false,
20133     role : false,
20134     sr_only: false,
20135     
20136     getAutoCreate : function()
20137     {
20138         
20139         var cfg = {
20140             tag: 'div',
20141             cls: 'progress-bar',
20142             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20143         };
20144         
20145         if(this.sr_only){
20146             cfg.cn = {
20147                 tag: 'span',
20148                 cls: 'sr-only',
20149                 html: this.sr_only
20150             }
20151         }
20152         
20153         if(this.role){
20154             cfg.role = this.role;
20155         }
20156         
20157         if(this.aria_valuenow){
20158             cfg['aria-valuenow'] = this.aria_valuenow;
20159         }
20160         
20161         if(this.aria_valuemin){
20162             cfg['aria-valuemin'] = this.aria_valuemin;
20163         }
20164         
20165         if(this.aria_valuemax){
20166             cfg['aria-valuemax'] = this.aria_valuemax;
20167         }
20168         
20169         if(this.label && !this.sr_only){
20170             cfg.html = this.label;
20171         }
20172         
20173         if(this.panel){
20174             cfg.cls += ' progress-bar-' + this.panel;
20175         }
20176         
20177         return cfg;
20178     },
20179     
20180     update : function(aria_valuenow)
20181     {
20182         this.aria_valuenow = aria_valuenow;
20183         
20184         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20185     }
20186    
20187 });
20188
20189  
20190
20191  /*
20192  * - LGPL
20193  *
20194  * column
20195  * 
20196  */
20197
20198 /**
20199  * @class Roo.bootstrap.TabGroup
20200  * @extends Roo.bootstrap.Column
20201  * Bootstrap Column class
20202  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20203  * @cfg {Boolean} carousel true to make the group behave like a carousel
20204  * @cfg {Boolean} bullets show bullets for the panels
20205  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20206  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20207  * @cfg {Boolean} showarrow (true|false) show arrow default true
20208  * 
20209  * @constructor
20210  * Create a new TabGroup
20211  * @param {Object} config The config object
20212  */
20213
20214 Roo.bootstrap.TabGroup = function(config){
20215     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20216     if (!this.navId) {
20217         this.navId = Roo.id();
20218     }
20219     this.tabs = [];
20220     Roo.bootstrap.TabGroup.register(this);
20221     
20222 };
20223
20224 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20225     
20226     carousel : false,
20227     transition : false,
20228     bullets : 0,
20229     timer : 0,
20230     autoslide : false,
20231     slideFn : false,
20232     slideOnTouch : false,
20233     showarrow : true,
20234     
20235     getAutoCreate : function()
20236     {
20237         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20238         
20239         cfg.cls += ' tab-content';
20240         
20241         if (this.carousel) {
20242             cfg.cls += ' carousel slide';
20243             
20244             cfg.cn = [{
20245                cls : 'carousel-inner',
20246                cn : []
20247             }];
20248         
20249             if(this.bullets  && !Roo.isTouch){
20250                 
20251                 var bullets = {
20252                     cls : 'carousel-bullets',
20253                     cn : []
20254                 };
20255                
20256                 if(this.bullets_cls){
20257                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20258                 }
20259                 
20260                 bullets.cn.push({
20261                     cls : 'clear'
20262                 });
20263                 
20264                 cfg.cn[0].cn.push(bullets);
20265             }
20266             
20267             if(this.showarrow){
20268                 cfg.cn[0].cn.push({
20269                     tag : 'div',
20270                     class : 'carousel-arrow',
20271                     cn : [
20272                         {
20273                             tag : 'div',
20274                             class : 'carousel-prev',
20275                             cn : [
20276                                 {
20277                                     tag : 'i',
20278                                     class : 'fa fa-chevron-left'
20279                                 }
20280                             ]
20281                         },
20282                         {
20283                             tag : 'div',
20284                             class : 'carousel-next',
20285                             cn : [
20286                                 {
20287                                     tag : 'i',
20288                                     class : 'fa fa-chevron-right'
20289                                 }
20290                             ]
20291                         }
20292                     ]
20293                 });
20294             }
20295             
20296         }
20297         
20298         return cfg;
20299     },
20300     
20301     initEvents:  function()
20302     {
20303 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20304 //            this.el.on("touchstart", this.onTouchStart, this);
20305 //        }
20306         
20307         if(this.autoslide){
20308             var _this = this;
20309             
20310             this.slideFn = window.setInterval(function() {
20311                 _this.showPanelNext();
20312             }, this.timer);
20313         }
20314         
20315         if(this.showarrow){
20316             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20317             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20318         }
20319         
20320         
20321     },
20322     
20323 //    onTouchStart : function(e, el, o)
20324 //    {
20325 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20326 //            return;
20327 //        }
20328 //        
20329 //        this.showPanelNext();
20330 //    },
20331     
20332     
20333     getChildContainer : function()
20334     {
20335         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20336     },
20337     
20338     /**
20339     * register a Navigation item
20340     * @param {Roo.bootstrap.NavItem} the navitem to add
20341     */
20342     register : function(item)
20343     {
20344         this.tabs.push( item);
20345         item.navId = this.navId; // not really needed..
20346         this.addBullet();
20347     
20348     },
20349     
20350     getActivePanel : function()
20351     {
20352         var r = false;
20353         Roo.each(this.tabs, function(t) {
20354             if (t.active) {
20355                 r = t;
20356                 return false;
20357             }
20358             return null;
20359         });
20360         return r;
20361         
20362     },
20363     getPanelByName : function(n)
20364     {
20365         var r = false;
20366         Roo.each(this.tabs, function(t) {
20367             if (t.tabId == n) {
20368                 r = t;
20369                 return false;
20370             }
20371             return null;
20372         });
20373         return r;
20374     },
20375     indexOfPanel : function(p)
20376     {
20377         var r = false;
20378         Roo.each(this.tabs, function(t,i) {
20379             if (t.tabId == p.tabId) {
20380                 r = i;
20381                 return false;
20382             }
20383             return null;
20384         });
20385         return r;
20386     },
20387     /**
20388      * show a specific panel
20389      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20390      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20391      */
20392     showPanel : function (pan)
20393     {
20394         if(this.transition || typeof(pan) == 'undefined'){
20395             Roo.log("waiting for the transitionend");
20396             return false;
20397         }
20398         
20399         if (typeof(pan) == 'number') {
20400             pan = this.tabs[pan];
20401         }
20402         
20403         if (typeof(pan) == 'string') {
20404             pan = this.getPanelByName(pan);
20405         }
20406         
20407         var cur = this.getActivePanel();
20408         
20409         if(!pan || !cur){
20410             Roo.log('pan or acitve pan is undefined');
20411             return false;
20412         }
20413         
20414         if (pan.tabId == this.getActivePanel().tabId) {
20415             return true;
20416         }
20417         
20418         if (false === cur.fireEvent('beforedeactivate')) {
20419             return false;
20420         }
20421         
20422         if(this.bullets > 0 && !Roo.isTouch){
20423             this.setActiveBullet(this.indexOfPanel(pan));
20424         }
20425         
20426         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20427             
20428             //class="carousel-item carousel-item-next carousel-item-left"
20429             
20430             this.transition = true;
20431             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20432             var lr = dir == 'next' ? 'left' : 'right';
20433             pan.el.addClass(dir); // or prev
20434             pan.el.addClass('carousel-item-' + dir); // or prev
20435             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20436             cur.el.addClass(lr); // or right
20437             pan.el.addClass(lr);
20438             cur.el.addClass('carousel-item-' +lr); // or right
20439             pan.el.addClass('carousel-item-' +lr);
20440             
20441             
20442             var _this = this;
20443             cur.el.on('transitionend', function() {
20444                 Roo.log("trans end?");
20445                 
20446                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20447                 pan.setActive(true);
20448                 
20449                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20450                 cur.setActive(false);
20451                 
20452                 _this.transition = false;
20453                 
20454             }, this, { single:  true } );
20455             
20456             return true;
20457         }
20458         
20459         cur.setActive(false);
20460         pan.setActive(true);
20461         
20462         return true;
20463         
20464     },
20465     showPanelNext : function()
20466     {
20467         var i = this.indexOfPanel(this.getActivePanel());
20468         
20469         if (i >= this.tabs.length - 1 && !this.autoslide) {
20470             return;
20471         }
20472         
20473         if (i >= this.tabs.length - 1 && this.autoslide) {
20474             i = -1;
20475         }
20476         
20477         this.showPanel(this.tabs[i+1]);
20478     },
20479     
20480     showPanelPrev : function()
20481     {
20482         var i = this.indexOfPanel(this.getActivePanel());
20483         
20484         if (i  < 1 && !this.autoslide) {
20485             return;
20486         }
20487         
20488         if (i < 1 && this.autoslide) {
20489             i = this.tabs.length;
20490         }
20491         
20492         this.showPanel(this.tabs[i-1]);
20493     },
20494     
20495     
20496     addBullet: function()
20497     {
20498         if(!this.bullets || Roo.isTouch){
20499             return;
20500         }
20501         var ctr = this.el.select('.carousel-bullets',true).first();
20502         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20503         var bullet = ctr.createChild({
20504             cls : 'bullet bullet-' + i
20505         },ctr.dom.lastChild);
20506         
20507         
20508         var _this = this;
20509         
20510         bullet.on('click', (function(e, el, o, ii, t){
20511
20512             e.preventDefault();
20513
20514             this.showPanel(ii);
20515
20516             if(this.autoslide && this.slideFn){
20517                 clearInterval(this.slideFn);
20518                 this.slideFn = window.setInterval(function() {
20519                     _this.showPanelNext();
20520                 }, this.timer);
20521             }
20522
20523         }).createDelegate(this, [i, bullet], true));
20524                 
20525         
20526     },
20527      
20528     setActiveBullet : function(i)
20529     {
20530         if(Roo.isTouch){
20531             return;
20532         }
20533         
20534         Roo.each(this.el.select('.bullet', true).elements, function(el){
20535             el.removeClass('selected');
20536         });
20537
20538         var bullet = this.el.select('.bullet-' + i, true).first();
20539         
20540         if(!bullet){
20541             return;
20542         }
20543         
20544         bullet.addClass('selected');
20545     }
20546     
20547     
20548   
20549 });
20550
20551  
20552
20553  
20554  
20555 Roo.apply(Roo.bootstrap.TabGroup, {
20556     
20557     groups: {},
20558      /**
20559     * register a Navigation Group
20560     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20561     */
20562     register : function(navgrp)
20563     {
20564         this.groups[navgrp.navId] = navgrp;
20565         
20566     },
20567     /**
20568     * fetch a Navigation Group based on the navigation ID
20569     * if one does not exist , it will get created.
20570     * @param {string} the navgroup to add
20571     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20572     */
20573     get: function(navId) {
20574         if (typeof(this.groups[navId]) == 'undefined') {
20575             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20576         }
20577         return this.groups[navId] ;
20578     }
20579     
20580     
20581     
20582 });
20583
20584  /*
20585  * - LGPL
20586  *
20587  * TabPanel
20588  * 
20589  */
20590
20591 /**
20592  * @class Roo.bootstrap.TabPanel
20593  * @extends Roo.bootstrap.Component
20594  * Bootstrap TabPanel class
20595  * @cfg {Boolean} active panel active
20596  * @cfg {String} html panel content
20597  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20598  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20599  * @cfg {String} href click to link..
20600  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20601  * 
20602  * 
20603  * @constructor
20604  * Create a new TabPanel
20605  * @param {Object} config The config object
20606  */
20607
20608 Roo.bootstrap.TabPanel = function(config){
20609     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20610     this.addEvents({
20611         /**
20612              * @event changed
20613              * Fires when the active status changes
20614              * @param {Roo.bootstrap.TabPanel} this
20615              * @param {Boolean} state the new state
20616             
20617          */
20618         'changed': true,
20619         /**
20620              * @event beforedeactivate
20621              * Fires before a tab is de-activated - can be used to do validation on a form.
20622              * @param {Roo.bootstrap.TabPanel} this
20623              * @return {Boolean} false if there is an error
20624             
20625          */
20626         'beforedeactivate': true
20627      });
20628     
20629     this.tabId = this.tabId || Roo.id();
20630   
20631 };
20632
20633 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20634     
20635     active: false,
20636     html: false,
20637     tabId: false,
20638     navId : false,
20639     href : '',
20640     touchSlide : false,
20641     getAutoCreate : function(){
20642         
20643         
20644         var cfg = {
20645             tag: 'div',
20646             // item is needed for carousel - not sure if it has any effect otherwise
20647             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20648             html: this.html || ''
20649         };
20650         
20651         if(this.active){
20652             cfg.cls += ' active';
20653         }
20654         
20655         if(this.tabId){
20656             cfg.tabId = this.tabId;
20657         }
20658         
20659         
20660         
20661         return cfg;
20662     },
20663     
20664     initEvents:  function()
20665     {
20666         var p = this.parent();
20667         
20668         this.navId = this.navId || p.navId;
20669         
20670         if (typeof(this.navId) != 'undefined') {
20671             // not really needed.. but just in case.. parent should be a NavGroup.
20672             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20673             
20674             tg.register(this);
20675             
20676             var i = tg.tabs.length - 1;
20677             
20678             if(this.active && tg.bullets > 0 && i < tg.bullets){
20679                 tg.setActiveBullet(i);
20680             }
20681         }
20682         
20683         this.el.on('click', this.onClick, this);
20684         
20685         if(Roo.isTouch && this.touchSlide){
20686             this.el.on("touchstart", this.onTouchStart, this);
20687             this.el.on("touchmove", this.onTouchMove, this);
20688             this.el.on("touchend", this.onTouchEnd, this);
20689         }
20690         
20691     },
20692     
20693     onRender : function(ct, position)
20694     {
20695         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20696     },
20697     
20698     setActive : function(state)
20699     {
20700         Roo.log("panel - set active " + this.tabId + "=" + state);
20701         
20702         this.active = state;
20703         if (!state) {
20704             this.el.removeClass('active');
20705             
20706         } else  if (!this.el.hasClass('active')) {
20707             this.el.addClass('active');
20708         }
20709         
20710         this.fireEvent('changed', this, state);
20711     },
20712     
20713     onClick : function(e)
20714     {
20715         e.preventDefault();
20716         
20717         if(!this.href.length){
20718             return;
20719         }
20720         
20721         window.location.href = this.href;
20722     },
20723     
20724     startX : 0,
20725     startY : 0,
20726     endX : 0,
20727     endY : 0,
20728     swiping : false,
20729     
20730     onTouchStart : function(e)
20731     {
20732         this.swiping = false;
20733         
20734         this.startX = e.browserEvent.touches[0].clientX;
20735         this.startY = e.browserEvent.touches[0].clientY;
20736     },
20737     
20738     onTouchMove : function(e)
20739     {
20740         this.swiping = true;
20741         
20742         this.endX = e.browserEvent.touches[0].clientX;
20743         this.endY = e.browserEvent.touches[0].clientY;
20744     },
20745     
20746     onTouchEnd : function(e)
20747     {
20748         if(!this.swiping){
20749             this.onClick(e);
20750             return;
20751         }
20752         
20753         var tabGroup = this.parent();
20754         
20755         if(this.endX > this.startX){ // swiping right
20756             tabGroup.showPanelPrev();
20757             return;
20758         }
20759         
20760         if(this.startX > this.endX){ // swiping left
20761             tabGroup.showPanelNext();
20762             return;
20763         }
20764     }
20765     
20766     
20767 });
20768  
20769
20770  
20771
20772  /*
20773  * - LGPL
20774  *
20775  * DateField
20776  * 
20777  */
20778
20779 /**
20780  * @class Roo.bootstrap.DateField
20781  * @extends Roo.bootstrap.Input
20782  * Bootstrap DateField class
20783  * @cfg {Number} weekStart default 0
20784  * @cfg {String} viewMode default empty, (months|years)
20785  * @cfg {String} minViewMode default empty, (months|years)
20786  * @cfg {Number} startDate default -Infinity
20787  * @cfg {Number} endDate default Infinity
20788  * @cfg {Boolean} todayHighlight default false
20789  * @cfg {Boolean} todayBtn default false
20790  * @cfg {Boolean} calendarWeeks default false
20791  * @cfg {Object} daysOfWeekDisabled default empty
20792  * @cfg {Boolean} singleMode default false (true | false)
20793  * 
20794  * @cfg {Boolean} keyboardNavigation default true
20795  * @cfg {String} language default en
20796  * 
20797  * @constructor
20798  * Create a new DateField
20799  * @param {Object} config The config object
20800  */
20801
20802 Roo.bootstrap.DateField = function(config){
20803     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20804      this.addEvents({
20805             /**
20806              * @event show
20807              * Fires when this field show.
20808              * @param {Roo.bootstrap.DateField} this
20809              * @param {Mixed} date The date value
20810              */
20811             show : true,
20812             /**
20813              * @event show
20814              * Fires when this field hide.
20815              * @param {Roo.bootstrap.DateField} this
20816              * @param {Mixed} date The date value
20817              */
20818             hide : true,
20819             /**
20820              * @event select
20821              * Fires when select a date.
20822              * @param {Roo.bootstrap.DateField} this
20823              * @param {Mixed} date The date value
20824              */
20825             select : true,
20826             /**
20827              * @event beforeselect
20828              * Fires when before select a date.
20829              * @param {Roo.bootstrap.DateField} this
20830              * @param {Mixed} date The date value
20831              */
20832             beforeselect : true
20833         });
20834 };
20835
20836 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20837     
20838     /**
20839      * @cfg {String} format
20840      * The default date format string which can be overriden for localization support.  The format must be
20841      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20842      */
20843     format : "m/d/y",
20844     /**
20845      * @cfg {String} altFormats
20846      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20847      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20848      */
20849     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20850     
20851     weekStart : 0,
20852     
20853     viewMode : '',
20854     
20855     minViewMode : '',
20856     
20857     todayHighlight : false,
20858     
20859     todayBtn: false,
20860     
20861     language: 'en',
20862     
20863     keyboardNavigation: true,
20864     
20865     calendarWeeks: false,
20866     
20867     startDate: -Infinity,
20868     
20869     endDate: Infinity,
20870     
20871     daysOfWeekDisabled: [],
20872     
20873     _events: [],
20874     
20875     singleMode : false,
20876     
20877     UTCDate: function()
20878     {
20879         return new Date(Date.UTC.apply(Date, arguments));
20880     },
20881     
20882     UTCToday: function()
20883     {
20884         var today = new Date();
20885         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20886     },
20887     
20888     getDate: function() {
20889             var d = this.getUTCDate();
20890             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20891     },
20892     
20893     getUTCDate: function() {
20894             return this.date;
20895     },
20896     
20897     setDate: function(d) {
20898             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20899     },
20900     
20901     setUTCDate: function(d) {
20902             this.date = d;
20903             this.setValue(this.formatDate(this.date));
20904     },
20905         
20906     onRender: function(ct, position)
20907     {
20908         
20909         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20910         
20911         this.language = this.language || 'en';
20912         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20913         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20914         
20915         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20916         this.format = this.format || 'm/d/y';
20917         this.isInline = false;
20918         this.isInput = true;
20919         this.component = this.el.select('.add-on', true).first() || false;
20920         this.component = (this.component && this.component.length === 0) ? false : this.component;
20921         this.hasInput = this.component && this.inputEl().length;
20922         
20923         if (typeof(this.minViewMode === 'string')) {
20924             switch (this.minViewMode) {
20925                 case 'months':
20926                     this.minViewMode = 1;
20927                     break;
20928                 case 'years':
20929                     this.minViewMode = 2;
20930                     break;
20931                 default:
20932                     this.minViewMode = 0;
20933                     break;
20934             }
20935         }
20936         
20937         if (typeof(this.viewMode === 'string')) {
20938             switch (this.viewMode) {
20939                 case 'months':
20940                     this.viewMode = 1;
20941                     break;
20942                 case 'years':
20943                     this.viewMode = 2;
20944                     break;
20945                 default:
20946                     this.viewMode = 0;
20947                     break;
20948             }
20949         }
20950                 
20951         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20952         
20953 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20954         
20955         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20956         
20957         this.picker().on('mousedown', this.onMousedown, this);
20958         this.picker().on('click', this.onClick, this);
20959         
20960         this.picker().addClass('datepicker-dropdown');
20961         
20962         this.startViewMode = this.viewMode;
20963         
20964         if(this.singleMode){
20965             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20966                 v.setVisibilityMode(Roo.Element.DISPLAY);
20967                 v.hide();
20968             });
20969             
20970             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20971                 v.setStyle('width', '189px');
20972             });
20973         }
20974         
20975         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20976             if(!this.calendarWeeks){
20977                 v.remove();
20978                 return;
20979             }
20980             
20981             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20982             v.attr('colspan', function(i, val){
20983                 return parseInt(val) + 1;
20984             });
20985         });
20986                         
20987         
20988         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20989         
20990         this.setStartDate(this.startDate);
20991         this.setEndDate(this.endDate);
20992         
20993         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20994         
20995         this.fillDow();
20996         this.fillMonths();
20997         this.update();
20998         this.showMode();
20999         
21000         if(this.isInline) {
21001             this.showPopup();
21002         }
21003     },
21004     
21005     picker : function()
21006     {
21007         return this.pickerEl;
21008 //        return this.el.select('.datepicker', true).first();
21009     },
21010     
21011     fillDow: function()
21012     {
21013         var dowCnt = this.weekStart;
21014         
21015         var dow = {
21016             tag: 'tr',
21017             cn: [
21018                 
21019             ]
21020         };
21021         
21022         if(this.calendarWeeks){
21023             dow.cn.push({
21024                 tag: 'th',
21025                 cls: 'cw',
21026                 html: '&nbsp;'
21027             })
21028         }
21029         
21030         while (dowCnt < this.weekStart + 7) {
21031             dow.cn.push({
21032                 tag: 'th',
21033                 cls: 'dow',
21034                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21035             });
21036         }
21037         
21038         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21039     },
21040     
21041     fillMonths: function()
21042     {    
21043         var i = 0;
21044         var months = this.picker().select('>.datepicker-months td', true).first();
21045         
21046         months.dom.innerHTML = '';
21047         
21048         while (i < 12) {
21049             var month = {
21050                 tag: 'span',
21051                 cls: 'month',
21052                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21053             };
21054             
21055             months.createChild(month);
21056         }
21057         
21058     },
21059     
21060     update: function()
21061     {
21062         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;
21063         
21064         if (this.date < this.startDate) {
21065             this.viewDate = new Date(this.startDate);
21066         } else if (this.date > this.endDate) {
21067             this.viewDate = new Date(this.endDate);
21068         } else {
21069             this.viewDate = new Date(this.date);
21070         }
21071         
21072         this.fill();
21073     },
21074     
21075     fill: function() 
21076     {
21077         var d = new Date(this.viewDate),
21078                 year = d.getUTCFullYear(),
21079                 month = d.getUTCMonth(),
21080                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21081                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21082                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21083                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21084                 currentDate = this.date && this.date.valueOf(),
21085                 today = this.UTCToday();
21086         
21087         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21088         
21089 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21090         
21091 //        this.picker.select('>tfoot th.today').
21092 //                                              .text(dates[this.language].today)
21093 //                                              .toggle(this.todayBtn !== false);
21094     
21095         this.updateNavArrows();
21096         this.fillMonths();
21097                                                 
21098         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21099         
21100         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21101          
21102         prevMonth.setUTCDate(day);
21103         
21104         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21105         
21106         var nextMonth = new Date(prevMonth);
21107         
21108         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21109         
21110         nextMonth = nextMonth.valueOf();
21111         
21112         var fillMonths = false;
21113         
21114         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21115         
21116         while(prevMonth.valueOf() <= nextMonth) {
21117             var clsName = '';
21118             
21119             if (prevMonth.getUTCDay() === this.weekStart) {
21120                 if(fillMonths){
21121                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21122                 }
21123                     
21124                 fillMonths = {
21125                     tag: 'tr',
21126                     cn: []
21127                 };
21128                 
21129                 if(this.calendarWeeks){
21130                     // ISO 8601: First week contains first thursday.
21131                     // ISO also states week starts on Monday, but we can be more abstract here.
21132                     var
21133                     // Start of current week: based on weekstart/current date
21134                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21135                     // Thursday of this week
21136                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21137                     // First Thursday of year, year from thursday
21138                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21139                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21140                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21141                     
21142                     fillMonths.cn.push({
21143                         tag: 'td',
21144                         cls: 'cw',
21145                         html: calWeek
21146                     });
21147                 }
21148             }
21149             
21150             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21151                 clsName += ' old';
21152             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21153                 clsName += ' new';
21154             }
21155             if (this.todayHighlight &&
21156                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21157                 prevMonth.getUTCMonth() == today.getMonth() &&
21158                 prevMonth.getUTCDate() == today.getDate()) {
21159                 clsName += ' today';
21160             }
21161             
21162             if (currentDate && prevMonth.valueOf() === currentDate) {
21163                 clsName += ' active';
21164             }
21165             
21166             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21167                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21168                     clsName += ' disabled';
21169             }
21170             
21171             fillMonths.cn.push({
21172                 tag: 'td',
21173                 cls: 'day ' + clsName,
21174                 html: prevMonth.getDate()
21175             });
21176             
21177             prevMonth.setDate(prevMonth.getDate()+1);
21178         }
21179           
21180         var currentYear = this.date && this.date.getUTCFullYear();
21181         var currentMonth = this.date && this.date.getUTCMonth();
21182         
21183         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21184         
21185         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21186             v.removeClass('active');
21187             
21188             if(currentYear === year && k === currentMonth){
21189                 v.addClass('active');
21190             }
21191             
21192             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21193                 v.addClass('disabled');
21194             }
21195             
21196         });
21197         
21198         
21199         year = parseInt(year/10, 10) * 10;
21200         
21201         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21202         
21203         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21204         
21205         year -= 1;
21206         for (var i = -1; i < 11; i++) {
21207             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21208                 tag: 'span',
21209                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21210                 html: year
21211             });
21212             
21213             year += 1;
21214         }
21215     },
21216     
21217     showMode: function(dir) 
21218     {
21219         if (dir) {
21220             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21221         }
21222         
21223         Roo.each(this.picker().select('>div',true).elements, function(v){
21224             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21225             v.hide();
21226         });
21227         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21228     },
21229     
21230     place: function()
21231     {
21232         if(this.isInline) {
21233             return;
21234         }
21235         
21236         this.picker().removeClass(['bottom', 'top']);
21237         
21238         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21239             /*
21240              * place to the top of element!
21241              *
21242              */
21243             
21244             this.picker().addClass('top');
21245             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21246             
21247             return;
21248         }
21249         
21250         this.picker().addClass('bottom');
21251         
21252         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21253     },
21254     
21255     parseDate : function(value)
21256     {
21257         if(!value || value instanceof Date){
21258             return value;
21259         }
21260         var v = Date.parseDate(value, this.format);
21261         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21262             v = Date.parseDate(value, 'Y-m-d');
21263         }
21264         if(!v && this.altFormats){
21265             if(!this.altFormatsArray){
21266                 this.altFormatsArray = this.altFormats.split("|");
21267             }
21268             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21269                 v = Date.parseDate(value, this.altFormatsArray[i]);
21270             }
21271         }
21272         return v;
21273     },
21274     
21275     formatDate : function(date, fmt)
21276     {   
21277         return (!date || !(date instanceof Date)) ?
21278         date : date.dateFormat(fmt || this.format);
21279     },
21280     
21281     onFocus : function()
21282     {
21283         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21284         this.showPopup();
21285     },
21286     
21287     onBlur : function()
21288     {
21289         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21290         
21291         var d = this.inputEl().getValue();
21292         
21293         this.setValue(d);
21294                 
21295         this.hidePopup();
21296     },
21297     
21298     showPopup : function()
21299     {
21300         this.picker().show();
21301         this.update();
21302         this.place();
21303         
21304         this.fireEvent('showpopup', this, this.date);
21305     },
21306     
21307     hidePopup : function()
21308     {
21309         if(this.isInline) {
21310             return;
21311         }
21312         this.picker().hide();
21313         this.viewMode = this.startViewMode;
21314         this.showMode();
21315         
21316         this.fireEvent('hidepopup', this, this.date);
21317         
21318     },
21319     
21320     onMousedown: function(e)
21321     {
21322         e.stopPropagation();
21323         e.preventDefault();
21324     },
21325     
21326     keyup: function(e)
21327     {
21328         Roo.bootstrap.DateField.superclass.keyup.call(this);
21329         this.update();
21330     },
21331
21332     setValue: function(v)
21333     {
21334         if(this.fireEvent('beforeselect', this, v) !== false){
21335             var d = new Date(this.parseDate(v) ).clearTime();
21336         
21337             if(isNaN(d.getTime())){
21338                 this.date = this.viewDate = '';
21339                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21340                 return;
21341             }
21342
21343             v = this.formatDate(d);
21344
21345             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21346
21347             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21348
21349             this.update();
21350
21351             this.fireEvent('select', this, this.date);
21352         }
21353     },
21354     
21355     getValue: function()
21356     {
21357         return this.formatDate(this.date);
21358     },
21359     
21360     fireKey: function(e)
21361     {
21362         if (!this.picker().isVisible()){
21363             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21364                 this.showPopup();
21365             }
21366             return;
21367         }
21368         
21369         var dateChanged = false,
21370         dir, day, month,
21371         newDate, newViewDate;
21372         
21373         switch(e.keyCode){
21374             case 27: // escape
21375                 this.hidePopup();
21376                 e.preventDefault();
21377                 break;
21378             case 37: // left
21379             case 39: // right
21380                 if (!this.keyboardNavigation) {
21381                     break;
21382                 }
21383                 dir = e.keyCode == 37 ? -1 : 1;
21384                 
21385                 if (e.ctrlKey){
21386                     newDate = this.moveYear(this.date, dir);
21387                     newViewDate = this.moveYear(this.viewDate, dir);
21388                 } else if (e.shiftKey){
21389                     newDate = this.moveMonth(this.date, dir);
21390                     newViewDate = this.moveMonth(this.viewDate, dir);
21391                 } else {
21392                     newDate = new Date(this.date);
21393                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21394                     newViewDate = new Date(this.viewDate);
21395                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21396                 }
21397                 if (this.dateWithinRange(newDate)){
21398                     this.date = newDate;
21399                     this.viewDate = newViewDate;
21400                     this.setValue(this.formatDate(this.date));
21401 //                    this.update();
21402                     e.preventDefault();
21403                     dateChanged = true;
21404                 }
21405                 break;
21406             case 38: // up
21407             case 40: // down
21408                 if (!this.keyboardNavigation) {
21409                     break;
21410                 }
21411                 dir = e.keyCode == 38 ? -1 : 1;
21412                 if (e.ctrlKey){
21413                     newDate = this.moveYear(this.date, dir);
21414                     newViewDate = this.moveYear(this.viewDate, dir);
21415                 } else if (e.shiftKey){
21416                     newDate = this.moveMonth(this.date, dir);
21417                     newViewDate = this.moveMonth(this.viewDate, dir);
21418                 } else {
21419                     newDate = new Date(this.date);
21420                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21421                     newViewDate = new Date(this.viewDate);
21422                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21423                 }
21424                 if (this.dateWithinRange(newDate)){
21425                     this.date = newDate;
21426                     this.viewDate = newViewDate;
21427                     this.setValue(this.formatDate(this.date));
21428 //                    this.update();
21429                     e.preventDefault();
21430                     dateChanged = true;
21431                 }
21432                 break;
21433             case 13: // enter
21434                 this.setValue(this.formatDate(this.date));
21435                 this.hidePopup();
21436                 e.preventDefault();
21437                 break;
21438             case 9: // tab
21439                 this.setValue(this.formatDate(this.date));
21440                 this.hidePopup();
21441                 break;
21442             case 16: // shift
21443             case 17: // ctrl
21444             case 18: // alt
21445                 break;
21446             default :
21447                 this.hidePopup();
21448                 
21449         }
21450     },
21451     
21452     
21453     onClick: function(e) 
21454     {
21455         e.stopPropagation();
21456         e.preventDefault();
21457         
21458         var target = e.getTarget();
21459         
21460         if(target.nodeName.toLowerCase() === 'i'){
21461             target = Roo.get(target).dom.parentNode;
21462         }
21463         
21464         var nodeName = target.nodeName;
21465         var className = target.className;
21466         var html = target.innerHTML;
21467         //Roo.log(nodeName);
21468         
21469         switch(nodeName.toLowerCase()) {
21470             case 'th':
21471                 switch(className) {
21472                     case 'switch':
21473                         this.showMode(1);
21474                         break;
21475                     case 'prev':
21476                     case 'next':
21477                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21478                         switch(this.viewMode){
21479                                 case 0:
21480                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21481                                         break;
21482                                 case 1:
21483                                 case 2:
21484                                         this.viewDate = this.moveYear(this.viewDate, dir);
21485                                         break;
21486                         }
21487                         this.fill();
21488                         break;
21489                     case 'today':
21490                         var date = new Date();
21491                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21492 //                        this.fill()
21493                         this.setValue(this.formatDate(this.date));
21494                         
21495                         this.hidePopup();
21496                         break;
21497                 }
21498                 break;
21499             case 'span':
21500                 if (className.indexOf('disabled') < 0) {
21501                     this.viewDate.setUTCDate(1);
21502                     if (className.indexOf('month') > -1) {
21503                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21504                     } else {
21505                         var year = parseInt(html, 10) || 0;
21506                         this.viewDate.setUTCFullYear(year);
21507                         
21508                     }
21509                     
21510                     if(this.singleMode){
21511                         this.setValue(this.formatDate(this.viewDate));
21512                         this.hidePopup();
21513                         return;
21514                     }
21515                     
21516                     this.showMode(-1);
21517                     this.fill();
21518                 }
21519                 break;
21520                 
21521             case 'td':
21522                 //Roo.log(className);
21523                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21524                     var day = parseInt(html, 10) || 1;
21525                     var year = this.viewDate.getUTCFullYear(),
21526                         month = this.viewDate.getUTCMonth();
21527
21528                     if (className.indexOf('old') > -1) {
21529                         if(month === 0 ){
21530                             month = 11;
21531                             year -= 1;
21532                         }else{
21533                             month -= 1;
21534                         }
21535                     } else if (className.indexOf('new') > -1) {
21536                         if (month == 11) {
21537                             month = 0;
21538                             year += 1;
21539                         } else {
21540                             month += 1;
21541                         }
21542                     }
21543                     //Roo.log([year,month,day]);
21544                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21545                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21546 //                    this.fill();
21547                     //Roo.log(this.formatDate(this.date));
21548                     this.setValue(this.formatDate(this.date));
21549                     this.hidePopup();
21550                 }
21551                 break;
21552         }
21553     },
21554     
21555     setStartDate: function(startDate)
21556     {
21557         this.startDate = startDate || -Infinity;
21558         if (this.startDate !== -Infinity) {
21559             this.startDate = this.parseDate(this.startDate);
21560         }
21561         this.update();
21562         this.updateNavArrows();
21563     },
21564
21565     setEndDate: function(endDate)
21566     {
21567         this.endDate = endDate || Infinity;
21568         if (this.endDate !== Infinity) {
21569             this.endDate = this.parseDate(this.endDate);
21570         }
21571         this.update();
21572         this.updateNavArrows();
21573     },
21574     
21575     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21576     {
21577         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21578         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21579             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21580         }
21581         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21582             return parseInt(d, 10);
21583         });
21584         this.update();
21585         this.updateNavArrows();
21586     },
21587     
21588     updateNavArrows: function() 
21589     {
21590         if(this.singleMode){
21591             return;
21592         }
21593         
21594         var d = new Date(this.viewDate),
21595         year = d.getUTCFullYear(),
21596         month = d.getUTCMonth();
21597         
21598         Roo.each(this.picker().select('.prev', true).elements, function(v){
21599             v.show();
21600             switch (this.viewMode) {
21601                 case 0:
21602
21603                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21604                         v.hide();
21605                     }
21606                     break;
21607                 case 1:
21608                 case 2:
21609                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21610                         v.hide();
21611                     }
21612                     break;
21613             }
21614         });
21615         
21616         Roo.each(this.picker().select('.next', true).elements, function(v){
21617             v.show();
21618             switch (this.viewMode) {
21619                 case 0:
21620
21621                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21622                         v.hide();
21623                     }
21624                     break;
21625                 case 1:
21626                 case 2:
21627                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21628                         v.hide();
21629                     }
21630                     break;
21631             }
21632         })
21633     },
21634     
21635     moveMonth: function(date, dir)
21636     {
21637         if (!dir) {
21638             return date;
21639         }
21640         var new_date = new Date(date.valueOf()),
21641         day = new_date.getUTCDate(),
21642         month = new_date.getUTCMonth(),
21643         mag = Math.abs(dir),
21644         new_month, test;
21645         dir = dir > 0 ? 1 : -1;
21646         if (mag == 1){
21647             test = dir == -1
21648             // If going back one month, make sure month is not current month
21649             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21650             ? function(){
21651                 return new_date.getUTCMonth() == month;
21652             }
21653             // If going forward one month, make sure month is as expected
21654             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21655             : function(){
21656                 return new_date.getUTCMonth() != new_month;
21657             };
21658             new_month = month + dir;
21659             new_date.setUTCMonth(new_month);
21660             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21661             if (new_month < 0 || new_month > 11) {
21662                 new_month = (new_month + 12) % 12;
21663             }
21664         } else {
21665             // For magnitudes >1, move one month at a time...
21666             for (var i=0; i<mag; i++) {
21667                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21668                 new_date = this.moveMonth(new_date, dir);
21669             }
21670             // ...then reset the day, keeping it in the new month
21671             new_month = new_date.getUTCMonth();
21672             new_date.setUTCDate(day);
21673             test = function(){
21674                 return new_month != new_date.getUTCMonth();
21675             };
21676         }
21677         // Common date-resetting loop -- if date is beyond end of month, make it
21678         // end of month
21679         while (test()){
21680             new_date.setUTCDate(--day);
21681             new_date.setUTCMonth(new_month);
21682         }
21683         return new_date;
21684     },
21685
21686     moveYear: function(date, dir)
21687     {
21688         return this.moveMonth(date, dir*12);
21689     },
21690
21691     dateWithinRange: function(date)
21692     {
21693         return date >= this.startDate && date <= this.endDate;
21694     },
21695
21696     
21697     remove: function() 
21698     {
21699         this.picker().remove();
21700     },
21701     
21702     validateValue : function(value)
21703     {
21704         if(this.getVisibilityEl().hasClass('hidden')){
21705             return true;
21706         }
21707         
21708         if(value.length < 1)  {
21709             if(this.allowBlank){
21710                 return true;
21711             }
21712             return false;
21713         }
21714         
21715         if(value.length < this.minLength){
21716             return false;
21717         }
21718         if(value.length > this.maxLength){
21719             return false;
21720         }
21721         if(this.vtype){
21722             var vt = Roo.form.VTypes;
21723             if(!vt[this.vtype](value, this)){
21724                 return false;
21725             }
21726         }
21727         if(typeof this.validator == "function"){
21728             var msg = this.validator(value);
21729             if(msg !== true){
21730                 return false;
21731             }
21732         }
21733         
21734         if(this.regex && !this.regex.test(value)){
21735             return false;
21736         }
21737         
21738         if(typeof(this.parseDate(value)) == 'undefined'){
21739             return false;
21740         }
21741         
21742         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21743             return false;
21744         }      
21745         
21746         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21747             return false;
21748         } 
21749         
21750         
21751         return true;
21752     },
21753     
21754     reset : function()
21755     {
21756         this.date = this.viewDate = '';
21757         
21758         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21759     }
21760    
21761 });
21762
21763 Roo.apply(Roo.bootstrap.DateField,  {
21764     
21765     head : {
21766         tag: 'thead',
21767         cn: [
21768         {
21769             tag: 'tr',
21770             cn: [
21771             {
21772                 tag: 'th',
21773                 cls: 'prev',
21774                 html: '<i class="fa fa-arrow-left"/>'
21775             },
21776             {
21777                 tag: 'th',
21778                 cls: 'switch',
21779                 colspan: '5'
21780             },
21781             {
21782                 tag: 'th',
21783                 cls: 'next',
21784                 html: '<i class="fa fa-arrow-right"/>'
21785             }
21786
21787             ]
21788         }
21789         ]
21790     },
21791     
21792     content : {
21793         tag: 'tbody',
21794         cn: [
21795         {
21796             tag: 'tr',
21797             cn: [
21798             {
21799                 tag: 'td',
21800                 colspan: '7'
21801             }
21802             ]
21803         }
21804         ]
21805     },
21806     
21807     footer : {
21808         tag: 'tfoot',
21809         cn: [
21810         {
21811             tag: 'tr',
21812             cn: [
21813             {
21814                 tag: 'th',
21815                 colspan: '7',
21816                 cls: 'today'
21817             }
21818                     
21819             ]
21820         }
21821         ]
21822     },
21823     
21824     dates:{
21825         en: {
21826             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21827             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21828             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21829             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21830             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21831             today: "Today"
21832         }
21833     },
21834     
21835     modes: [
21836     {
21837         clsName: 'days',
21838         navFnc: 'Month',
21839         navStep: 1
21840     },
21841     {
21842         clsName: 'months',
21843         navFnc: 'FullYear',
21844         navStep: 1
21845     },
21846     {
21847         clsName: 'years',
21848         navFnc: 'FullYear',
21849         navStep: 10
21850     }]
21851 });
21852
21853 Roo.apply(Roo.bootstrap.DateField,  {
21854   
21855     template : {
21856         tag: 'div',
21857         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21858         cn: [
21859         {
21860             tag: 'div',
21861             cls: 'datepicker-days',
21862             cn: [
21863             {
21864                 tag: 'table',
21865                 cls: 'table-condensed',
21866                 cn:[
21867                 Roo.bootstrap.DateField.head,
21868                 {
21869                     tag: 'tbody'
21870                 },
21871                 Roo.bootstrap.DateField.footer
21872                 ]
21873             }
21874             ]
21875         },
21876         {
21877             tag: 'div',
21878             cls: 'datepicker-months',
21879             cn: [
21880             {
21881                 tag: 'table',
21882                 cls: 'table-condensed',
21883                 cn:[
21884                 Roo.bootstrap.DateField.head,
21885                 Roo.bootstrap.DateField.content,
21886                 Roo.bootstrap.DateField.footer
21887                 ]
21888             }
21889             ]
21890         },
21891         {
21892             tag: 'div',
21893             cls: 'datepicker-years',
21894             cn: [
21895             {
21896                 tag: 'table',
21897                 cls: 'table-condensed',
21898                 cn:[
21899                 Roo.bootstrap.DateField.head,
21900                 Roo.bootstrap.DateField.content,
21901                 Roo.bootstrap.DateField.footer
21902                 ]
21903             }
21904             ]
21905         }
21906         ]
21907     }
21908 });
21909
21910  
21911
21912  /*
21913  * - LGPL
21914  *
21915  * TimeField
21916  * 
21917  */
21918
21919 /**
21920  * @class Roo.bootstrap.TimeField
21921  * @extends Roo.bootstrap.Input
21922  * Bootstrap DateField class
21923  * 
21924  * 
21925  * @constructor
21926  * Create a new TimeField
21927  * @param {Object} config The config object
21928  */
21929
21930 Roo.bootstrap.TimeField = function(config){
21931     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21932     this.addEvents({
21933             /**
21934              * @event show
21935              * Fires when this field show.
21936              * @param {Roo.bootstrap.DateField} thisthis
21937              * @param {Mixed} date The date value
21938              */
21939             show : true,
21940             /**
21941              * @event show
21942              * Fires when this field hide.
21943              * @param {Roo.bootstrap.DateField} this
21944              * @param {Mixed} date The date value
21945              */
21946             hide : true,
21947             /**
21948              * @event select
21949              * Fires when select a date.
21950              * @param {Roo.bootstrap.DateField} this
21951              * @param {Mixed} date The date value
21952              */
21953             select : true
21954         });
21955 };
21956
21957 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21958     
21959     /**
21960      * @cfg {String} format
21961      * The default time format string which can be overriden for localization support.  The format must be
21962      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21963      */
21964     format : "H:i",
21965
21966     getAutoCreate : function()
21967     {
21968         this.after = '<i class="fa far fa-clock"></i>';
21969         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
21970         
21971          
21972     },
21973     onRender: function(ct, position)
21974     {
21975         
21976         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21977                 
21978         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
21979         
21980         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21981         
21982         this.pop = this.picker().select('>.datepicker-time',true).first();
21983         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21984         
21985         this.picker().on('mousedown', this.onMousedown, this);
21986         this.picker().on('click', this.onClick, this);
21987         
21988         this.picker().addClass('datepicker-dropdown');
21989     
21990         this.fillTime();
21991         this.update();
21992             
21993         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
21994         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
21995         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21996         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21997         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21998         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21999
22000     },
22001     
22002     fireKey: function(e){
22003         if (!this.picker().isVisible()){
22004             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22005                 this.show();
22006             }
22007             return;
22008         }
22009
22010         e.preventDefault();
22011         
22012         switch(e.keyCode){
22013             case 27: // escape
22014                 this.hide();
22015                 break;
22016             case 37: // left
22017             case 39: // right
22018                 this.onTogglePeriod();
22019                 break;
22020             case 38: // up
22021                 this.onIncrementMinutes();
22022                 break;
22023             case 40: // down
22024                 this.onDecrementMinutes();
22025                 break;
22026             case 13: // enter
22027             case 9: // tab
22028                 this.setTime();
22029                 break;
22030         }
22031     },
22032     
22033     onClick: function(e) {
22034         e.stopPropagation();
22035         e.preventDefault();
22036     },
22037     
22038     picker : function()
22039     {
22040         return this.pickerEl;
22041     },
22042     
22043     fillTime: function()
22044     {    
22045         var time = this.pop.select('tbody', true).first();
22046         
22047         time.dom.innerHTML = '';
22048         
22049         time.createChild({
22050             tag: 'tr',
22051             cn: [
22052                 {
22053                     tag: 'td',
22054                     cn: [
22055                         {
22056                             tag: 'a',
22057                             href: '#',
22058                             cls: 'btn',
22059                             cn: [
22060                                 {
22061                                     tag: 'i',
22062                                     cls: 'hours-up fa fas fa-chevron-up'
22063                                 }
22064                             ]
22065                         } 
22066                     ]
22067                 },
22068                 {
22069                     tag: 'td',
22070                     cls: 'separator'
22071                 },
22072                 {
22073                     tag: 'td',
22074                     cn: [
22075                         {
22076                             tag: 'a',
22077                             href: '#',
22078                             cls: 'btn',
22079                             cn: [
22080                                 {
22081                                     tag: 'i',
22082                                     cls: 'minutes-up fa fas fa-chevron-up'
22083                                 }
22084                             ]
22085                         }
22086                     ]
22087                 },
22088                 {
22089                     tag: 'td',
22090                     cls: 'separator'
22091                 }
22092             ]
22093         });
22094         
22095         time.createChild({
22096             tag: 'tr',
22097             cn: [
22098                 {
22099                     tag: 'td',
22100                     cn: [
22101                         {
22102                             tag: 'span',
22103                             cls: 'timepicker-hour',
22104                             html: '00'
22105                         }  
22106                     ]
22107                 },
22108                 {
22109                     tag: 'td',
22110                     cls: 'separator',
22111                     html: ':'
22112                 },
22113                 {
22114                     tag: 'td',
22115                     cn: [
22116                         {
22117                             tag: 'span',
22118                             cls: 'timepicker-minute',
22119                             html: '00'
22120                         }  
22121                     ]
22122                 },
22123                 {
22124                     tag: 'td',
22125                     cls: 'separator'
22126                 },
22127                 {
22128                     tag: 'td',
22129                     cn: [
22130                         {
22131                             tag: 'button',
22132                             type: 'button',
22133                             cls: 'btn btn-primary period',
22134                             html: 'AM'
22135                             
22136                         }
22137                     ]
22138                 }
22139             ]
22140         });
22141         
22142         time.createChild({
22143             tag: 'tr',
22144             cn: [
22145                 {
22146                     tag: 'td',
22147                     cn: [
22148                         {
22149                             tag: 'a',
22150                             href: '#',
22151                             cls: 'btn',
22152                             cn: [
22153                                 {
22154                                     tag: 'span',
22155                                     cls: 'hours-down fa fas fa-chevron-down'
22156                                 }
22157                             ]
22158                         }
22159                     ]
22160                 },
22161                 {
22162                     tag: 'td',
22163                     cls: 'separator'
22164                 },
22165                 {
22166                     tag: 'td',
22167                     cn: [
22168                         {
22169                             tag: 'a',
22170                             href: '#',
22171                             cls: 'btn',
22172                             cn: [
22173                                 {
22174                                     tag: 'span',
22175                                     cls: 'minutes-down fa fas fa-chevron-down'
22176                                 }
22177                             ]
22178                         }
22179                     ]
22180                 },
22181                 {
22182                     tag: 'td',
22183                     cls: 'separator'
22184                 }
22185             ]
22186         });
22187         
22188     },
22189     
22190     update: function()
22191     {
22192         
22193         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22194         
22195         this.fill();
22196     },
22197     
22198     fill: function() 
22199     {
22200         var hours = this.time.getHours();
22201         var minutes = this.time.getMinutes();
22202         var period = 'AM';
22203         
22204         if(hours > 11){
22205             period = 'PM';
22206         }
22207         
22208         if(hours == 0){
22209             hours = 12;
22210         }
22211         
22212         
22213         if(hours > 12){
22214             hours = hours - 12;
22215         }
22216         
22217         if(hours < 10){
22218             hours = '0' + hours;
22219         }
22220         
22221         if(minutes < 10){
22222             minutes = '0' + minutes;
22223         }
22224         
22225         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22226         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22227         this.pop.select('button', true).first().dom.innerHTML = period;
22228         
22229     },
22230     
22231     place: function()
22232     {   
22233         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22234         
22235         var cls = ['bottom'];
22236         
22237         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22238             cls.pop();
22239             cls.push('top');
22240         }
22241         
22242         cls.push('right');
22243         
22244         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22245             cls.pop();
22246             cls.push('left');
22247         }
22248         
22249         this.picker().addClass(cls.join('-'));
22250         
22251         var _this = this;
22252         
22253         Roo.each(cls, function(c){
22254             if(c == 'bottom'){
22255                 _this.picker().alignTo(_this.inputEl(), 't-b');
22256                 
22257                 //_this.picker().setTop(_this.inputEl().getHeight());
22258                 return;
22259             }
22260             if(c == 'top'){
22261                 _this.picker().setTop(0 - _this.picker().getHeight());
22262                 return;
22263             }
22264             
22265             if(c == 'left'){
22266                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22267                 return;
22268             }
22269             if(c == 'right'){
22270                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22271                 return;
22272             }
22273         });
22274         
22275     },
22276   
22277     onFocus : function()
22278     {
22279         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22280         this.show();
22281     },
22282     
22283     onBlur : function()
22284     {
22285         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22286         this.hide();
22287     },
22288     
22289     show : function()
22290     {
22291         this.picker().show();
22292         this.pop.show();
22293         this.update();
22294         this.place();
22295         
22296         this.fireEvent('show', this, this.date);
22297     },
22298     
22299     hide : function()
22300     {
22301         this.picker().hide();
22302         this.pop.hide();
22303         
22304         this.fireEvent('hide', this, this.date);
22305     },
22306     
22307     setTime : function()
22308     {
22309         this.hide();
22310         this.setValue(this.time.format(this.format));
22311         
22312         this.fireEvent('select', this, this.date);
22313         
22314         
22315     },
22316     
22317     onMousedown: function(e){
22318         e.stopPropagation();
22319         e.preventDefault();
22320     },
22321     
22322     onIncrementHours: function()
22323     {
22324         Roo.log('onIncrementHours');
22325         this.time = this.time.add(Date.HOUR, 1);
22326         this.update();
22327         
22328     },
22329     
22330     onDecrementHours: function()
22331     {
22332         Roo.log('onDecrementHours');
22333         this.time = this.time.add(Date.HOUR, -1);
22334         this.update();
22335     },
22336     
22337     onIncrementMinutes: function()
22338     {
22339         Roo.log('onIncrementMinutes');
22340         this.time = this.time.add(Date.MINUTE, 1);
22341         this.update();
22342     },
22343     
22344     onDecrementMinutes: function()
22345     {
22346         Roo.log('onDecrementMinutes');
22347         this.time = this.time.add(Date.MINUTE, -1);
22348         this.update();
22349     },
22350     
22351     onTogglePeriod: function()
22352     {
22353         Roo.log('onTogglePeriod');
22354         this.time = this.time.add(Date.HOUR, 12);
22355         this.update();
22356     }
22357     
22358    
22359 });
22360  
22361
22362 Roo.apply(Roo.bootstrap.TimeField,  {
22363   
22364     template : {
22365         tag: 'div',
22366         cls: 'datepicker dropdown-menu',
22367         cn: [
22368             {
22369                 tag: 'div',
22370                 cls: 'datepicker-time',
22371                 cn: [
22372                 {
22373                     tag: 'table',
22374                     cls: 'table-condensed',
22375                     cn:[
22376                         {
22377                             tag: 'tbody',
22378                             cn: [
22379                                 {
22380                                     tag: 'tr',
22381                                     cn: [
22382                                     {
22383                                         tag: 'td',
22384                                         colspan: '7'
22385                                     }
22386                                     ]
22387                                 }
22388                             ]
22389                         },
22390                         {
22391                             tag: 'tfoot',
22392                             cn: [
22393                                 {
22394                                     tag: 'tr',
22395                                     cn: [
22396                                     {
22397                                         tag: 'th',
22398                                         colspan: '7',
22399                                         cls: '',
22400                                         cn: [
22401                                             {
22402                                                 tag: 'button',
22403                                                 cls: 'btn btn-info ok',
22404                                                 html: 'OK'
22405                                             }
22406                                         ]
22407                                     }
22408                     
22409                                     ]
22410                                 }
22411                             ]
22412                         }
22413                     ]
22414                 }
22415                 ]
22416             }
22417         ]
22418     }
22419 });
22420
22421  
22422
22423  /*
22424  * - LGPL
22425  *
22426  * MonthField
22427  * 
22428  */
22429
22430 /**
22431  * @class Roo.bootstrap.MonthField
22432  * @extends Roo.bootstrap.Input
22433  * Bootstrap MonthField class
22434  * 
22435  * @cfg {String} language default en
22436  * 
22437  * @constructor
22438  * Create a new MonthField
22439  * @param {Object} config The config object
22440  */
22441
22442 Roo.bootstrap.MonthField = function(config){
22443     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22444     
22445     this.addEvents({
22446         /**
22447          * @event show
22448          * Fires when this field show.
22449          * @param {Roo.bootstrap.MonthField} this
22450          * @param {Mixed} date The date value
22451          */
22452         show : true,
22453         /**
22454          * @event show
22455          * Fires when this field hide.
22456          * @param {Roo.bootstrap.MonthField} this
22457          * @param {Mixed} date The date value
22458          */
22459         hide : true,
22460         /**
22461          * @event select
22462          * Fires when select a date.
22463          * @param {Roo.bootstrap.MonthField} this
22464          * @param {String} oldvalue The old value
22465          * @param {String} newvalue The new value
22466          */
22467         select : true
22468     });
22469 };
22470
22471 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22472     
22473     onRender: function(ct, position)
22474     {
22475         
22476         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22477         
22478         this.language = this.language || 'en';
22479         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22480         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22481         
22482         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22483         this.isInline = false;
22484         this.isInput = true;
22485         this.component = this.el.select('.add-on', true).first() || false;
22486         this.component = (this.component && this.component.length === 0) ? false : this.component;
22487         this.hasInput = this.component && this.inputEL().length;
22488         
22489         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22490         
22491         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22492         
22493         this.picker().on('mousedown', this.onMousedown, this);
22494         this.picker().on('click', this.onClick, this);
22495         
22496         this.picker().addClass('datepicker-dropdown');
22497         
22498         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22499             v.setStyle('width', '189px');
22500         });
22501         
22502         this.fillMonths();
22503         
22504         this.update();
22505         
22506         if(this.isInline) {
22507             this.show();
22508         }
22509         
22510     },
22511     
22512     setValue: function(v, suppressEvent)
22513     {   
22514         var o = this.getValue();
22515         
22516         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22517         
22518         this.update();
22519
22520         if(suppressEvent !== true){
22521             this.fireEvent('select', this, o, v);
22522         }
22523         
22524     },
22525     
22526     getValue: function()
22527     {
22528         return this.value;
22529     },
22530     
22531     onClick: function(e) 
22532     {
22533         e.stopPropagation();
22534         e.preventDefault();
22535         
22536         var target = e.getTarget();
22537         
22538         if(target.nodeName.toLowerCase() === 'i'){
22539             target = Roo.get(target).dom.parentNode;
22540         }
22541         
22542         var nodeName = target.nodeName;
22543         var className = target.className;
22544         var html = target.innerHTML;
22545         
22546         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22547             return;
22548         }
22549         
22550         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22551         
22552         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22553         
22554         this.hide();
22555                         
22556     },
22557     
22558     picker : function()
22559     {
22560         return this.pickerEl;
22561     },
22562     
22563     fillMonths: function()
22564     {    
22565         var i = 0;
22566         var months = this.picker().select('>.datepicker-months td', true).first();
22567         
22568         months.dom.innerHTML = '';
22569         
22570         while (i < 12) {
22571             var month = {
22572                 tag: 'span',
22573                 cls: 'month',
22574                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22575             };
22576             
22577             months.createChild(month);
22578         }
22579         
22580     },
22581     
22582     update: function()
22583     {
22584         var _this = this;
22585         
22586         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22587             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22588         }
22589         
22590         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22591             e.removeClass('active');
22592             
22593             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22594                 e.addClass('active');
22595             }
22596         })
22597     },
22598     
22599     place: function()
22600     {
22601         if(this.isInline) {
22602             return;
22603         }
22604         
22605         this.picker().removeClass(['bottom', 'top']);
22606         
22607         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22608             /*
22609              * place to the top of element!
22610              *
22611              */
22612             
22613             this.picker().addClass('top');
22614             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22615             
22616             return;
22617         }
22618         
22619         this.picker().addClass('bottom');
22620         
22621         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22622     },
22623     
22624     onFocus : function()
22625     {
22626         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22627         this.show();
22628     },
22629     
22630     onBlur : function()
22631     {
22632         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22633         
22634         var d = this.inputEl().getValue();
22635         
22636         this.setValue(d);
22637                 
22638         this.hide();
22639     },
22640     
22641     show : function()
22642     {
22643         this.picker().show();
22644         this.picker().select('>.datepicker-months', true).first().show();
22645         this.update();
22646         this.place();
22647         
22648         this.fireEvent('show', this, this.date);
22649     },
22650     
22651     hide : function()
22652     {
22653         if(this.isInline) {
22654             return;
22655         }
22656         this.picker().hide();
22657         this.fireEvent('hide', this, this.date);
22658         
22659     },
22660     
22661     onMousedown: function(e)
22662     {
22663         e.stopPropagation();
22664         e.preventDefault();
22665     },
22666     
22667     keyup: function(e)
22668     {
22669         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22670         this.update();
22671     },
22672
22673     fireKey: function(e)
22674     {
22675         if (!this.picker().isVisible()){
22676             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22677                 this.show();
22678             }
22679             return;
22680         }
22681         
22682         var dir;
22683         
22684         switch(e.keyCode){
22685             case 27: // escape
22686                 this.hide();
22687                 e.preventDefault();
22688                 break;
22689             case 37: // left
22690             case 39: // right
22691                 dir = e.keyCode == 37 ? -1 : 1;
22692                 
22693                 this.vIndex = this.vIndex + dir;
22694                 
22695                 if(this.vIndex < 0){
22696                     this.vIndex = 0;
22697                 }
22698                 
22699                 if(this.vIndex > 11){
22700                     this.vIndex = 11;
22701                 }
22702                 
22703                 if(isNaN(this.vIndex)){
22704                     this.vIndex = 0;
22705                 }
22706                 
22707                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22708                 
22709                 break;
22710             case 38: // up
22711             case 40: // down
22712                 
22713                 dir = e.keyCode == 38 ? -1 : 1;
22714                 
22715                 this.vIndex = this.vIndex + dir * 4;
22716                 
22717                 if(this.vIndex < 0){
22718                     this.vIndex = 0;
22719                 }
22720                 
22721                 if(this.vIndex > 11){
22722                     this.vIndex = 11;
22723                 }
22724                 
22725                 if(isNaN(this.vIndex)){
22726                     this.vIndex = 0;
22727                 }
22728                 
22729                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22730                 break;
22731                 
22732             case 13: // enter
22733                 
22734                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22735                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22736                 }
22737                 
22738                 this.hide();
22739                 e.preventDefault();
22740                 break;
22741             case 9: // tab
22742                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22743                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22744                 }
22745                 this.hide();
22746                 break;
22747             case 16: // shift
22748             case 17: // ctrl
22749             case 18: // alt
22750                 break;
22751             default :
22752                 this.hide();
22753                 
22754         }
22755     },
22756     
22757     remove: function() 
22758     {
22759         this.picker().remove();
22760     }
22761    
22762 });
22763
22764 Roo.apply(Roo.bootstrap.MonthField,  {
22765     
22766     content : {
22767         tag: 'tbody',
22768         cn: [
22769         {
22770             tag: 'tr',
22771             cn: [
22772             {
22773                 tag: 'td',
22774                 colspan: '7'
22775             }
22776             ]
22777         }
22778         ]
22779     },
22780     
22781     dates:{
22782         en: {
22783             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22784             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22785         }
22786     }
22787 });
22788
22789 Roo.apply(Roo.bootstrap.MonthField,  {
22790   
22791     template : {
22792         tag: 'div',
22793         cls: 'datepicker dropdown-menu roo-dynamic',
22794         cn: [
22795             {
22796                 tag: 'div',
22797                 cls: 'datepicker-months',
22798                 cn: [
22799                 {
22800                     tag: 'table',
22801                     cls: 'table-condensed',
22802                     cn:[
22803                         Roo.bootstrap.DateField.content
22804                     ]
22805                 }
22806                 ]
22807             }
22808         ]
22809     }
22810 });
22811
22812  
22813
22814  
22815  /*
22816  * - LGPL
22817  *
22818  * CheckBox
22819  * 
22820  */
22821
22822 /**
22823  * @class Roo.bootstrap.CheckBox
22824  * @extends Roo.bootstrap.Input
22825  * Bootstrap CheckBox class
22826  * 
22827  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22828  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22829  * @cfg {String} boxLabel The text that appears beside the checkbox
22830  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22831  * @cfg {Boolean} checked initnal the element
22832  * @cfg {Boolean} inline inline the element (default false)
22833  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22834  * @cfg {String} tooltip label tooltip
22835  * 
22836  * @constructor
22837  * Create a new CheckBox
22838  * @param {Object} config The config object
22839  */
22840
22841 Roo.bootstrap.CheckBox = function(config){
22842     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22843    
22844     this.addEvents({
22845         /**
22846         * @event check
22847         * Fires when the element is checked or unchecked.
22848         * @param {Roo.bootstrap.CheckBox} this This input
22849         * @param {Boolean} checked The new checked value
22850         */
22851        check : true,
22852        /**
22853         * @event click
22854         * Fires when the element is click.
22855         * @param {Roo.bootstrap.CheckBox} this This input
22856         */
22857        click : true
22858     });
22859     
22860 };
22861
22862 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22863   
22864     inputType: 'checkbox',
22865     inputValue: 1,
22866     valueOff: 0,
22867     boxLabel: false,
22868     checked: false,
22869     weight : false,
22870     inline: false,
22871     tooltip : '',
22872     
22873     // checkbox success does not make any sense really.. 
22874     invalidClass : "",
22875     validClass : "",
22876     
22877     
22878     getAutoCreate : function()
22879     {
22880         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22881         
22882         var id = Roo.id();
22883         
22884         var cfg = {};
22885         
22886         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22887         
22888         if(this.inline){
22889             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22890         }
22891         
22892         var input =  {
22893             tag: 'input',
22894             id : id,
22895             type : this.inputType,
22896             value : this.inputValue,
22897             cls : 'roo-' + this.inputType, //'form-box',
22898             placeholder : this.placeholder || ''
22899             
22900         };
22901         
22902         if(this.inputType != 'radio'){
22903             var hidden =  {
22904                 tag: 'input',
22905                 type : 'hidden',
22906                 cls : 'roo-hidden-value',
22907                 value : this.checked ? this.inputValue : this.valueOff
22908             };
22909         }
22910         
22911             
22912         if (this.weight) { // Validity check?
22913             cfg.cls += " " + this.inputType + "-" + this.weight;
22914         }
22915         
22916         if (this.disabled) {
22917             input.disabled=true;
22918         }
22919         
22920         if(this.checked){
22921             input.checked = this.checked;
22922         }
22923         
22924         if (this.name) {
22925             
22926             input.name = this.name;
22927             
22928             if(this.inputType != 'radio'){
22929                 hidden.name = this.name;
22930                 input.name = '_hidden_' + this.name;
22931             }
22932         }
22933         
22934         if (this.size) {
22935             input.cls += ' input-' + this.size;
22936         }
22937         
22938         var settings=this;
22939         
22940         ['xs','sm','md','lg'].map(function(size){
22941             if (settings[size]) {
22942                 cfg.cls += ' col-' + size + '-' + settings[size];
22943             }
22944         });
22945         
22946         var inputblock = input;
22947          
22948         if (this.before || this.after) {
22949             
22950             inputblock = {
22951                 cls : 'input-group',
22952                 cn :  [] 
22953             };
22954             
22955             if (this.before) {
22956                 inputblock.cn.push({
22957                     tag :'span',
22958                     cls : 'input-group-addon',
22959                     html : this.before
22960                 });
22961             }
22962             
22963             inputblock.cn.push(input);
22964             
22965             if(this.inputType != 'radio'){
22966                 inputblock.cn.push(hidden);
22967             }
22968             
22969             if (this.after) {
22970                 inputblock.cn.push({
22971                     tag :'span',
22972                     cls : 'input-group-addon',
22973                     html : this.after
22974                 });
22975             }
22976             
22977         }
22978         var boxLabelCfg = false;
22979         
22980         if(this.boxLabel){
22981            
22982             boxLabelCfg = {
22983                 tag: 'label',
22984                 //'for': id, // box label is handled by onclick - so no for...
22985                 cls: 'box-label',
22986                 html: this.boxLabel
22987             };
22988             if(this.tooltip){
22989                 boxLabelCfg.tooltip = this.tooltip;
22990             }
22991              
22992         }
22993         
22994         
22995         if (align ==='left' && this.fieldLabel.length) {
22996 //                Roo.log("left and has label");
22997             cfg.cn = [
22998                 {
22999                     tag: 'label',
23000                     'for' :  id,
23001                     cls : 'control-label',
23002                     html : this.fieldLabel
23003                 },
23004                 {
23005                     cls : "", 
23006                     cn: [
23007                         inputblock
23008                     ]
23009                 }
23010             ];
23011             
23012             if (boxLabelCfg) {
23013                 cfg.cn[1].cn.push(boxLabelCfg);
23014             }
23015             
23016             if(this.labelWidth > 12){
23017                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23018             }
23019             
23020             if(this.labelWidth < 13 && this.labelmd == 0){
23021                 this.labelmd = this.labelWidth;
23022             }
23023             
23024             if(this.labellg > 0){
23025                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23026                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23027             }
23028             
23029             if(this.labelmd > 0){
23030                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23031                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23032             }
23033             
23034             if(this.labelsm > 0){
23035                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23036                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23037             }
23038             
23039             if(this.labelxs > 0){
23040                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23041                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23042             }
23043             
23044         } else if ( this.fieldLabel.length) {
23045 //                Roo.log(" label");
23046                 cfg.cn = [
23047                    
23048                     {
23049                         tag: this.boxLabel ? 'span' : 'label',
23050                         'for': id,
23051                         cls: 'control-label box-input-label',
23052                         //cls : 'input-group-addon',
23053                         html : this.fieldLabel
23054                     },
23055                     
23056                     inputblock
23057                     
23058                 ];
23059                 if (boxLabelCfg) {
23060                     cfg.cn.push(boxLabelCfg);
23061                 }
23062
23063         } else {
23064             
23065 //                Roo.log(" no label && no align");
23066                 cfg.cn = [  inputblock ] ;
23067                 if (boxLabelCfg) {
23068                     cfg.cn.push(boxLabelCfg);
23069                 }
23070
23071                 
23072         }
23073         
23074        
23075         
23076         if(this.inputType != 'radio'){
23077             cfg.cn.push(hidden);
23078         }
23079         
23080         return cfg;
23081         
23082     },
23083     
23084     /**
23085      * return the real input element.
23086      */
23087     inputEl: function ()
23088     {
23089         return this.el.select('input.roo-' + this.inputType,true).first();
23090     },
23091     hiddenEl: function ()
23092     {
23093         return this.el.select('input.roo-hidden-value',true).first();
23094     },
23095     
23096     labelEl: function()
23097     {
23098         return this.el.select('label.control-label',true).first();
23099     },
23100     /* depricated... */
23101     
23102     label: function()
23103     {
23104         return this.labelEl();
23105     },
23106     
23107     boxLabelEl: function()
23108     {
23109         return this.el.select('label.box-label',true).first();
23110     },
23111     
23112     initEvents : function()
23113     {
23114 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23115         
23116         this.inputEl().on('click', this.onClick,  this);
23117         
23118         if (this.boxLabel) { 
23119             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23120         }
23121         
23122         this.startValue = this.getValue();
23123         
23124         if(this.groupId){
23125             Roo.bootstrap.CheckBox.register(this);
23126         }
23127     },
23128     
23129     onClick : function(e)
23130     {   
23131         if(this.fireEvent('click', this, e) !== false){
23132             this.setChecked(!this.checked);
23133         }
23134         
23135     },
23136     
23137     setChecked : function(state,suppressEvent)
23138     {
23139         this.startValue = this.getValue();
23140
23141         if(this.inputType == 'radio'){
23142             
23143             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23144                 e.dom.checked = false;
23145             });
23146             
23147             this.inputEl().dom.checked = true;
23148             
23149             this.inputEl().dom.value = this.inputValue;
23150             
23151             if(suppressEvent !== true){
23152                 this.fireEvent('check', this, true);
23153             }
23154             
23155             this.validate();
23156             
23157             return;
23158         }
23159         
23160         this.checked = state;
23161         
23162         this.inputEl().dom.checked = state;
23163         
23164         
23165         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23166         
23167         if(suppressEvent !== true){
23168             this.fireEvent('check', this, state);
23169         }
23170         
23171         this.validate();
23172     },
23173     
23174     getValue : function()
23175     {
23176         if(this.inputType == 'radio'){
23177             return this.getGroupValue();
23178         }
23179         
23180         return this.hiddenEl().dom.value;
23181         
23182     },
23183     
23184     getGroupValue : function()
23185     {
23186         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23187             return '';
23188         }
23189         
23190         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23191     },
23192     
23193     setValue : function(v,suppressEvent)
23194     {
23195         if(this.inputType == 'radio'){
23196             this.setGroupValue(v, suppressEvent);
23197             return;
23198         }
23199         
23200         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23201         
23202         this.validate();
23203     },
23204     
23205     setGroupValue : function(v, suppressEvent)
23206     {
23207         this.startValue = this.getValue();
23208         
23209         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23210             e.dom.checked = false;
23211             
23212             if(e.dom.value == v){
23213                 e.dom.checked = true;
23214             }
23215         });
23216         
23217         if(suppressEvent !== true){
23218             this.fireEvent('check', this, true);
23219         }
23220
23221         this.validate();
23222         
23223         return;
23224     },
23225     
23226     validate : function()
23227     {
23228         if(this.getVisibilityEl().hasClass('hidden')){
23229             return true;
23230         }
23231         
23232         if(
23233                 this.disabled || 
23234                 (this.inputType == 'radio' && this.validateRadio()) ||
23235                 (this.inputType == 'checkbox' && this.validateCheckbox())
23236         ){
23237             this.markValid();
23238             return true;
23239         }
23240         
23241         this.markInvalid();
23242         return false;
23243     },
23244     
23245     validateRadio : function()
23246     {
23247         if(this.getVisibilityEl().hasClass('hidden')){
23248             return true;
23249         }
23250         
23251         if(this.allowBlank){
23252             return true;
23253         }
23254         
23255         var valid = false;
23256         
23257         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23258             if(!e.dom.checked){
23259                 return;
23260             }
23261             
23262             valid = true;
23263             
23264             return false;
23265         });
23266         
23267         return valid;
23268     },
23269     
23270     validateCheckbox : function()
23271     {
23272         if(!this.groupId){
23273             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23274             //return (this.getValue() == this.inputValue) ? true : false;
23275         }
23276         
23277         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23278         
23279         if(!group){
23280             return false;
23281         }
23282         
23283         var r = false;
23284         
23285         for(var i in group){
23286             if(group[i].el.isVisible(true)){
23287                 r = false;
23288                 break;
23289             }
23290             
23291             r = true;
23292         }
23293         
23294         for(var i in group){
23295             if(r){
23296                 break;
23297             }
23298             
23299             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23300         }
23301         
23302         return r;
23303     },
23304     
23305     /**
23306      * Mark this field as valid
23307      */
23308     markValid : function()
23309     {
23310         var _this = this;
23311         
23312         this.fireEvent('valid', this);
23313         
23314         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23315         
23316         if(this.groupId){
23317             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23318         }
23319         
23320         if(label){
23321             label.markValid();
23322         }
23323
23324         if(this.inputType == 'radio'){
23325             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23326                 var fg = e.findParent('.form-group', false, true);
23327                 if (Roo.bootstrap.version == 3) {
23328                     fg.removeClass([_this.invalidClass, _this.validClass]);
23329                     fg.addClass(_this.validClass);
23330                 } else {
23331                     fg.removeClass(['is-valid', 'is-invalid']);
23332                     fg.addClass('is-valid');
23333                 }
23334             });
23335             
23336             return;
23337         }
23338
23339         if(!this.groupId){
23340             var fg = this.el.findParent('.form-group', false, true);
23341             if (Roo.bootstrap.version == 3) {
23342                 fg.removeClass([this.invalidClass, this.validClass]);
23343                 fg.addClass(this.validClass);
23344             } else {
23345                 fg.removeClass(['is-valid', 'is-invalid']);
23346                 fg.addClass('is-valid');
23347             }
23348             return;
23349         }
23350         
23351         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23352         
23353         if(!group){
23354             return;
23355         }
23356         
23357         for(var i in group){
23358             var fg = group[i].el.findParent('.form-group', false, true);
23359             if (Roo.bootstrap.version == 3) {
23360                 fg.removeClass([this.invalidClass, this.validClass]);
23361                 fg.addClass(this.validClass);
23362             } else {
23363                 fg.removeClass(['is-valid', 'is-invalid']);
23364                 fg.addClass('is-valid');
23365             }
23366         }
23367     },
23368     
23369      /**
23370      * Mark this field as invalid
23371      * @param {String} msg The validation message
23372      */
23373     markInvalid : function(msg)
23374     {
23375         if(this.allowBlank){
23376             return;
23377         }
23378         
23379         var _this = this;
23380         
23381         this.fireEvent('invalid', this, msg);
23382         
23383         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23384         
23385         if(this.groupId){
23386             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23387         }
23388         
23389         if(label){
23390             label.markInvalid();
23391         }
23392             
23393         if(this.inputType == 'radio'){
23394             
23395             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23396                 var fg = e.findParent('.form-group', false, true);
23397                 if (Roo.bootstrap.version == 3) {
23398                     fg.removeClass([_this.invalidClass, _this.validClass]);
23399                     fg.addClass(_this.invalidClass);
23400                 } else {
23401                     fg.removeClass(['is-invalid', 'is-valid']);
23402                     fg.addClass('is-invalid');
23403                 }
23404             });
23405             
23406             return;
23407         }
23408         
23409         if(!this.groupId){
23410             var fg = this.el.findParent('.form-group', false, true);
23411             if (Roo.bootstrap.version == 3) {
23412                 fg.removeClass([_this.invalidClass, _this.validClass]);
23413                 fg.addClass(_this.invalidClass);
23414             } else {
23415                 fg.removeClass(['is-invalid', 'is-valid']);
23416                 fg.addClass('is-invalid');
23417             }
23418             return;
23419         }
23420         
23421         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23422         
23423         if(!group){
23424             return;
23425         }
23426         
23427         for(var i in group){
23428             var fg = group[i].el.findParent('.form-group', false, true);
23429             if (Roo.bootstrap.version == 3) {
23430                 fg.removeClass([_this.invalidClass, _this.validClass]);
23431                 fg.addClass(_this.invalidClass);
23432             } else {
23433                 fg.removeClass(['is-invalid', 'is-valid']);
23434                 fg.addClass('is-invalid');
23435             }
23436         }
23437         
23438     },
23439     
23440     clearInvalid : function()
23441     {
23442         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23443         
23444         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23445         
23446         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23447         
23448         if (label && label.iconEl) {
23449             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23450             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23451         }
23452     },
23453     
23454     disable : function()
23455     {
23456         if(this.inputType != 'radio'){
23457             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23458             return;
23459         }
23460         
23461         var _this = this;
23462         
23463         if(this.rendered){
23464             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23465                 _this.getActionEl().addClass(this.disabledClass);
23466                 e.dom.disabled = true;
23467             });
23468         }
23469         
23470         this.disabled = true;
23471         this.fireEvent("disable", this);
23472         return this;
23473     },
23474
23475     enable : function()
23476     {
23477         if(this.inputType != 'radio'){
23478             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23479             return;
23480         }
23481         
23482         var _this = this;
23483         
23484         if(this.rendered){
23485             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23486                 _this.getActionEl().removeClass(this.disabledClass);
23487                 e.dom.disabled = false;
23488             });
23489         }
23490         
23491         this.disabled = false;
23492         this.fireEvent("enable", this);
23493         return this;
23494     },
23495     
23496     setBoxLabel : function(v)
23497     {
23498         this.boxLabel = v;
23499         
23500         if(this.rendered){
23501             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23502         }
23503     }
23504
23505 });
23506
23507 Roo.apply(Roo.bootstrap.CheckBox, {
23508     
23509     groups: {},
23510     
23511      /**
23512     * register a CheckBox Group
23513     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23514     */
23515     register : function(checkbox)
23516     {
23517         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23518             this.groups[checkbox.groupId] = {};
23519         }
23520         
23521         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23522             return;
23523         }
23524         
23525         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23526         
23527     },
23528     /**
23529     * fetch a CheckBox Group based on the group ID
23530     * @param {string} the group ID
23531     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23532     */
23533     get: function(groupId) {
23534         if (typeof(this.groups[groupId]) == 'undefined') {
23535             return false;
23536         }
23537         
23538         return this.groups[groupId] ;
23539     }
23540     
23541     
23542 });
23543 /*
23544  * - LGPL
23545  *
23546  * RadioItem
23547  * 
23548  */
23549
23550 /**
23551  * @class Roo.bootstrap.Radio
23552  * @extends Roo.bootstrap.Component
23553  * Bootstrap Radio class
23554  * @cfg {String} boxLabel - the label associated
23555  * @cfg {String} value - the value of radio
23556  * 
23557  * @constructor
23558  * Create a new Radio
23559  * @param {Object} config The config object
23560  */
23561 Roo.bootstrap.Radio = function(config){
23562     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23563     
23564 };
23565
23566 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23567     
23568     boxLabel : '',
23569     
23570     value : '',
23571     
23572     getAutoCreate : function()
23573     {
23574         var cfg = {
23575             tag : 'div',
23576             cls : 'form-group radio',
23577             cn : [
23578                 {
23579                     tag : 'label',
23580                     cls : 'box-label',
23581                     html : this.boxLabel
23582                 }
23583             ]
23584         };
23585         
23586         return cfg;
23587     },
23588     
23589     initEvents : function() 
23590     {
23591         this.parent().register(this);
23592         
23593         this.el.on('click', this.onClick, this);
23594         
23595     },
23596     
23597     onClick : function(e)
23598     {
23599         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23600             this.setChecked(true);
23601         }
23602     },
23603     
23604     setChecked : function(state, suppressEvent)
23605     {
23606         this.parent().setValue(this.value, suppressEvent);
23607         
23608     },
23609     
23610     setBoxLabel : function(v)
23611     {
23612         this.boxLabel = v;
23613         
23614         if(this.rendered){
23615             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23616         }
23617     }
23618     
23619 });
23620  
23621
23622  /*
23623  * - LGPL
23624  *
23625  * Input
23626  * 
23627  */
23628
23629 /**
23630  * @class Roo.bootstrap.SecurePass
23631  * @extends Roo.bootstrap.Input
23632  * Bootstrap SecurePass class
23633  *
23634  * 
23635  * @constructor
23636  * Create a new SecurePass
23637  * @param {Object} config The config object
23638  */
23639  
23640 Roo.bootstrap.SecurePass = function (config) {
23641     // these go here, so the translation tool can replace them..
23642     this.errors = {
23643         PwdEmpty: "Please type a password, and then retype it to confirm.",
23644         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23645         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23646         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23647         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23648         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23649         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23650         TooWeak: "Your password is Too Weak."
23651     },
23652     this.meterLabel = "Password strength:";
23653     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23654     this.meterClass = [
23655         "roo-password-meter-tooweak", 
23656         "roo-password-meter-weak", 
23657         "roo-password-meter-medium", 
23658         "roo-password-meter-strong", 
23659         "roo-password-meter-grey"
23660     ];
23661     
23662     this.errors = {};
23663     
23664     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23665 }
23666
23667 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23668     /**
23669      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23670      * {
23671      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23672      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23673      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23674      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23675      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23676      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23677      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23678      * })
23679      */
23680     // private
23681     
23682     meterWidth: 300,
23683     errorMsg :'',    
23684     errors: false,
23685     imageRoot: '/',
23686     /**
23687      * @cfg {String/Object} Label for the strength meter (defaults to
23688      * 'Password strength:')
23689      */
23690     // private
23691     meterLabel: '',
23692     /**
23693      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23694      * ['Weak', 'Medium', 'Strong'])
23695      */
23696     // private    
23697     pwdStrengths: false,    
23698     // private
23699     strength: 0,
23700     // private
23701     _lastPwd: null,
23702     // private
23703     kCapitalLetter: 0,
23704     kSmallLetter: 1,
23705     kDigit: 2,
23706     kPunctuation: 3,
23707     
23708     insecure: false,
23709     // private
23710     initEvents: function ()
23711     {
23712         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23713
23714         if (this.el.is('input[type=password]') && Roo.isSafari) {
23715             this.el.on('keydown', this.SafariOnKeyDown, this);
23716         }
23717
23718         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23719     },
23720     // private
23721     onRender: function (ct, position)
23722     {
23723         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23724         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23725         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23726
23727         this.trigger.createChild({
23728                    cn: [
23729                     {
23730                     //id: 'PwdMeter',
23731                     tag: 'div',
23732                     cls: 'roo-password-meter-grey col-xs-12',
23733                     style: {
23734                         //width: 0,
23735                         //width: this.meterWidth + 'px'                                                
23736                         }
23737                     },
23738                     {                            
23739                          cls: 'roo-password-meter-text'                          
23740                     }
23741                 ]            
23742         });
23743
23744          
23745         if (this.hideTrigger) {
23746             this.trigger.setDisplayed(false);
23747         }
23748         this.setSize(this.width || '', this.height || '');
23749     },
23750     // private
23751     onDestroy: function ()
23752     {
23753         if (this.trigger) {
23754             this.trigger.removeAllListeners();
23755             this.trigger.remove();
23756         }
23757         if (this.wrap) {
23758             this.wrap.remove();
23759         }
23760         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23761     },
23762     // private
23763     checkStrength: function ()
23764     {
23765         var pwd = this.inputEl().getValue();
23766         if (pwd == this._lastPwd) {
23767             return;
23768         }
23769
23770         var strength;
23771         if (this.ClientSideStrongPassword(pwd)) {
23772             strength = 3;
23773         } else if (this.ClientSideMediumPassword(pwd)) {
23774             strength = 2;
23775         } else if (this.ClientSideWeakPassword(pwd)) {
23776             strength = 1;
23777         } else {
23778             strength = 0;
23779         }
23780         
23781         Roo.log('strength1: ' + strength);
23782         
23783         //var pm = this.trigger.child('div/div/div').dom;
23784         var pm = this.trigger.child('div/div');
23785         pm.removeClass(this.meterClass);
23786         pm.addClass(this.meterClass[strength]);
23787                 
23788         
23789         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23790                 
23791         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23792         
23793         this._lastPwd = pwd;
23794     },
23795     reset: function ()
23796     {
23797         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23798         
23799         this._lastPwd = '';
23800         
23801         var pm = this.trigger.child('div/div');
23802         pm.removeClass(this.meterClass);
23803         pm.addClass('roo-password-meter-grey');        
23804         
23805         
23806         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23807         
23808         pt.innerHTML = '';
23809         this.inputEl().dom.type='password';
23810     },
23811     // private
23812     validateValue: function (value)
23813     {
23814         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23815             return false;
23816         }
23817         if (value.length == 0) {
23818             if (this.allowBlank) {
23819                 this.clearInvalid();
23820                 return true;
23821             }
23822
23823             this.markInvalid(this.errors.PwdEmpty);
23824             this.errorMsg = this.errors.PwdEmpty;
23825             return false;
23826         }
23827         
23828         if(this.insecure){
23829             return true;
23830         }
23831         
23832         if (!value.match(/[\x21-\x7e]+/)) {
23833             this.markInvalid(this.errors.PwdBadChar);
23834             this.errorMsg = this.errors.PwdBadChar;
23835             return false;
23836         }
23837         if (value.length < 6) {
23838             this.markInvalid(this.errors.PwdShort);
23839             this.errorMsg = this.errors.PwdShort;
23840             return false;
23841         }
23842         if (value.length > 16) {
23843             this.markInvalid(this.errors.PwdLong);
23844             this.errorMsg = this.errors.PwdLong;
23845             return false;
23846         }
23847         var strength;
23848         if (this.ClientSideStrongPassword(value)) {
23849             strength = 3;
23850         } else if (this.ClientSideMediumPassword(value)) {
23851             strength = 2;
23852         } else if (this.ClientSideWeakPassword(value)) {
23853             strength = 1;
23854         } else {
23855             strength = 0;
23856         }
23857
23858         
23859         if (strength < 2) {
23860             //this.markInvalid(this.errors.TooWeak);
23861             this.errorMsg = this.errors.TooWeak;
23862             //return false;
23863         }
23864         
23865         
23866         console.log('strength2: ' + strength);
23867         
23868         //var pm = this.trigger.child('div/div/div').dom;
23869         
23870         var pm = this.trigger.child('div/div');
23871         pm.removeClass(this.meterClass);
23872         pm.addClass(this.meterClass[strength]);
23873                 
23874         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23875                 
23876         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23877         
23878         this.errorMsg = ''; 
23879         return true;
23880     },
23881     // private
23882     CharacterSetChecks: function (type)
23883     {
23884         this.type = type;
23885         this.fResult = false;
23886     },
23887     // private
23888     isctype: function (character, type)
23889     {
23890         switch (type) {  
23891             case this.kCapitalLetter:
23892                 if (character >= 'A' && character <= 'Z') {
23893                     return true;
23894                 }
23895                 break;
23896             
23897             case this.kSmallLetter:
23898                 if (character >= 'a' && character <= 'z') {
23899                     return true;
23900                 }
23901                 break;
23902             
23903             case this.kDigit:
23904                 if (character >= '0' && character <= '9') {
23905                     return true;
23906                 }
23907                 break;
23908             
23909             case this.kPunctuation:
23910                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23911                     return true;
23912                 }
23913                 break;
23914             
23915             default:
23916                 return false;
23917         }
23918
23919     },
23920     // private
23921     IsLongEnough: function (pwd, size)
23922     {
23923         return !(pwd == null || isNaN(size) || pwd.length < size);
23924     },
23925     // private
23926     SpansEnoughCharacterSets: function (word, nb)
23927     {
23928         if (!this.IsLongEnough(word, nb))
23929         {
23930             return false;
23931         }
23932
23933         var characterSetChecks = new Array(
23934             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23935             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23936         );
23937         
23938         for (var index = 0; index < word.length; ++index) {
23939             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23940                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23941                     characterSetChecks[nCharSet].fResult = true;
23942                     break;
23943                 }
23944             }
23945         }
23946
23947         var nCharSets = 0;
23948         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23949             if (characterSetChecks[nCharSet].fResult) {
23950                 ++nCharSets;
23951             }
23952         }
23953
23954         if (nCharSets < nb) {
23955             return false;
23956         }
23957         return true;
23958     },
23959     // private
23960     ClientSideStrongPassword: function (pwd)
23961     {
23962         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23963     },
23964     // private
23965     ClientSideMediumPassword: function (pwd)
23966     {
23967         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23968     },
23969     // private
23970     ClientSideWeakPassword: function (pwd)
23971     {
23972         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23973     }
23974           
23975 })//<script type="text/javascript">
23976
23977 /*
23978  * Based  Ext JS Library 1.1.1
23979  * Copyright(c) 2006-2007, Ext JS, LLC.
23980  * LGPL
23981  *
23982  */
23983  
23984 /**
23985  * @class Roo.HtmlEditorCore
23986  * @extends Roo.Component
23987  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23988  *
23989  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23990  */
23991
23992 Roo.HtmlEditorCore = function(config){
23993     
23994     
23995     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23996     
23997     
23998     this.addEvents({
23999         /**
24000          * @event initialize
24001          * Fires when the editor is fully initialized (including the iframe)
24002          * @param {Roo.HtmlEditorCore} this
24003          */
24004         initialize: true,
24005         /**
24006          * @event activate
24007          * Fires when the editor is first receives the focus. Any insertion must wait
24008          * until after this event.
24009          * @param {Roo.HtmlEditorCore} this
24010          */
24011         activate: true,
24012          /**
24013          * @event beforesync
24014          * Fires before the textarea is updated with content from the editor iframe. Return false
24015          * to cancel the sync.
24016          * @param {Roo.HtmlEditorCore} this
24017          * @param {String} html
24018          */
24019         beforesync: true,
24020          /**
24021          * @event beforepush
24022          * Fires before the iframe editor is updated with content from the textarea. Return false
24023          * to cancel the push.
24024          * @param {Roo.HtmlEditorCore} this
24025          * @param {String} html
24026          */
24027         beforepush: true,
24028          /**
24029          * @event sync
24030          * Fires when the textarea is updated with content from the editor iframe.
24031          * @param {Roo.HtmlEditorCore} this
24032          * @param {String} html
24033          */
24034         sync: true,
24035          /**
24036          * @event push
24037          * Fires when the iframe editor is updated with content from the textarea.
24038          * @param {Roo.HtmlEditorCore} this
24039          * @param {String} html
24040          */
24041         push: true,
24042         
24043         /**
24044          * @event editorevent
24045          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24046          * @param {Roo.HtmlEditorCore} this
24047          */
24048         editorevent: true
24049         
24050     });
24051     
24052     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24053     
24054     // defaults : white / black...
24055     this.applyBlacklists();
24056     
24057     
24058     
24059 };
24060
24061
24062 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24063
24064
24065      /**
24066      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24067      */
24068     
24069     owner : false,
24070     
24071      /**
24072      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24073      *                        Roo.resizable.
24074      */
24075     resizable : false,
24076      /**
24077      * @cfg {Number} height (in pixels)
24078      */   
24079     height: 300,
24080    /**
24081      * @cfg {Number} width (in pixels)
24082      */   
24083     width: 500,
24084     
24085     /**
24086      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24087      * 
24088      */
24089     stylesheets: false,
24090     
24091     // id of frame..
24092     frameId: false,
24093     
24094     // private properties
24095     validationEvent : false,
24096     deferHeight: true,
24097     initialized : false,
24098     activated : false,
24099     sourceEditMode : false,
24100     onFocus : Roo.emptyFn,
24101     iframePad:3,
24102     hideMode:'offsets',
24103     
24104     clearUp: true,
24105     
24106     // blacklist + whitelisted elements..
24107     black: false,
24108     white: false,
24109      
24110     bodyCls : '',
24111
24112     /**
24113      * Protected method that will not generally be called directly. It
24114      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24115      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24116      */
24117     getDocMarkup : function(){
24118         // body styles..
24119         var st = '';
24120         
24121         // inherit styels from page...?? 
24122         if (this.stylesheets === false) {
24123             
24124             Roo.get(document.head).select('style').each(function(node) {
24125                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24126             });
24127             
24128             Roo.get(document.head).select('link').each(function(node) { 
24129                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24130             });
24131             
24132         } else if (!this.stylesheets.length) {
24133                 // simple..
24134                 st = '<style type="text/css">' +
24135                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24136                    '</style>';
24137         } else {
24138             for (var i in this.stylesheets) { 
24139                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24140             }
24141             
24142         }
24143         
24144         st +=  '<style type="text/css">' +
24145             'IMG { cursor: pointer } ' +
24146         '</style>';
24147
24148         var cls = 'roo-htmleditor-body';
24149         
24150         if(this.bodyCls.length){
24151             cls += ' ' + this.bodyCls;
24152         }
24153         
24154         return '<html><head>' + st  +
24155             //<style type="text/css">' +
24156             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24157             //'</style>' +
24158             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24159     },
24160
24161     // private
24162     onRender : function(ct, position)
24163     {
24164         var _t = this;
24165         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24166         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24167         
24168         
24169         this.el.dom.style.border = '0 none';
24170         this.el.dom.setAttribute('tabIndex', -1);
24171         this.el.addClass('x-hidden hide');
24172         
24173         
24174         
24175         if(Roo.isIE){ // fix IE 1px bogus margin
24176             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24177         }
24178        
24179         
24180         this.frameId = Roo.id();
24181         
24182          
24183         
24184         var iframe = this.owner.wrap.createChild({
24185             tag: 'iframe',
24186             cls: 'form-control', // bootstrap..
24187             id: this.frameId,
24188             name: this.frameId,
24189             frameBorder : 'no',
24190             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24191         }, this.el
24192         );
24193         
24194         
24195         this.iframe = iframe.dom;
24196
24197          this.assignDocWin();
24198         
24199         this.doc.designMode = 'on';
24200        
24201         this.doc.open();
24202         this.doc.write(this.getDocMarkup());
24203         this.doc.close();
24204
24205         
24206         var task = { // must defer to wait for browser to be ready
24207             run : function(){
24208                 //console.log("run task?" + this.doc.readyState);
24209                 this.assignDocWin();
24210                 if(this.doc.body || this.doc.readyState == 'complete'){
24211                     try {
24212                         this.doc.designMode="on";
24213                     } catch (e) {
24214                         return;
24215                     }
24216                     Roo.TaskMgr.stop(task);
24217                     this.initEditor.defer(10, this);
24218                 }
24219             },
24220             interval : 10,
24221             duration: 10000,
24222             scope: this
24223         };
24224         Roo.TaskMgr.start(task);
24225
24226     },
24227
24228     // private
24229     onResize : function(w, h)
24230     {
24231          Roo.log('resize: ' +w + ',' + h );
24232         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24233         if(!this.iframe){
24234             return;
24235         }
24236         if(typeof w == 'number'){
24237             
24238             this.iframe.style.width = w + 'px';
24239         }
24240         if(typeof h == 'number'){
24241             
24242             this.iframe.style.height = h + 'px';
24243             if(this.doc){
24244                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24245             }
24246         }
24247         
24248     },
24249
24250     /**
24251      * Toggles the editor between standard and source edit mode.
24252      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24253      */
24254     toggleSourceEdit : function(sourceEditMode){
24255         
24256         this.sourceEditMode = sourceEditMode === true;
24257         
24258         if(this.sourceEditMode){
24259  
24260             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24261             
24262         }else{
24263             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24264             //this.iframe.className = '';
24265             this.deferFocus();
24266         }
24267         //this.setSize(this.owner.wrap.getSize());
24268         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24269     },
24270
24271     
24272   
24273
24274     /**
24275      * Protected method that will not generally be called directly. If you need/want
24276      * custom HTML cleanup, this is the method you should override.
24277      * @param {String} html The HTML to be cleaned
24278      * return {String} The cleaned HTML
24279      */
24280     cleanHtml : function(html){
24281         html = String(html);
24282         if(html.length > 5){
24283             if(Roo.isSafari){ // strip safari nonsense
24284                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24285             }
24286         }
24287         if(html == '&nbsp;'){
24288             html = '';
24289         }
24290         return html;
24291     },
24292
24293     /**
24294      * HTML Editor -> Textarea
24295      * Protected method that will not generally be called directly. Syncs the contents
24296      * of the editor iframe with the textarea.
24297      */
24298     syncValue : function(){
24299         if(this.initialized){
24300             var bd = (this.doc.body || this.doc.documentElement);
24301             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24302             var html = bd.innerHTML;
24303             if(Roo.isSafari){
24304                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24305                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24306                 if(m && m[1]){
24307                     html = '<div style="'+m[0]+'">' + html + '</div>';
24308                 }
24309             }
24310             html = this.cleanHtml(html);
24311             // fix up the special chars.. normaly like back quotes in word...
24312             // however we do not want to do this with chinese..
24313             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24314                 
24315                 var cc = match.charCodeAt();
24316
24317                 // Get the character value, handling surrogate pairs
24318                 if (match.length == 2) {
24319                     // It's a surrogate pair, calculate the Unicode code point
24320                     var high = match.charCodeAt(0) - 0xD800;
24321                     var low  = match.charCodeAt(1) - 0xDC00;
24322                     cc = (high * 0x400) + low + 0x10000;
24323                 }  else if (
24324                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24325                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24326                     (cc >= 0xf900 && cc < 0xfb00 )
24327                 ) {
24328                         return match;
24329                 }  
24330          
24331                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24332                 return "&#" + cc + ";";
24333                 
24334                 
24335             });
24336             
24337             
24338              
24339             if(this.owner.fireEvent('beforesync', this, html) !== false){
24340                 this.el.dom.value = html;
24341                 this.owner.fireEvent('sync', this, html);
24342             }
24343         }
24344     },
24345
24346     /**
24347      * Protected method that will not generally be called directly. Pushes the value of the textarea
24348      * into the iframe editor.
24349      */
24350     pushValue : function(){
24351         if(this.initialized){
24352             var v = this.el.dom.value.trim();
24353             
24354 //            if(v.length < 1){
24355 //                v = '&#160;';
24356 //            }
24357             
24358             if(this.owner.fireEvent('beforepush', this, v) !== false){
24359                 var d = (this.doc.body || this.doc.documentElement);
24360                 d.innerHTML = v;
24361                 this.cleanUpPaste();
24362                 this.el.dom.value = d.innerHTML;
24363                 this.owner.fireEvent('push', this, v);
24364             }
24365         }
24366     },
24367
24368     // private
24369     deferFocus : function(){
24370         this.focus.defer(10, this);
24371     },
24372
24373     // doc'ed in Field
24374     focus : function(){
24375         if(this.win && !this.sourceEditMode){
24376             this.win.focus();
24377         }else{
24378             this.el.focus();
24379         }
24380     },
24381     
24382     assignDocWin: function()
24383     {
24384         var iframe = this.iframe;
24385         
24386          if(Roo.isIE){
24387             this.doc = iframe.contentWindow.document;
24388             this.win = iframe.contentWindow;
24389         } else {
24390 //            if (!Roo.get(this.frameId)) {
24391 //                return;
24392 //            }
24393 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24394 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24395             
24396             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24397                 return;
24398             }
24399             
24400             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24401             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24402         }
24403     },
24404     
24405     // private
24406     initEditor : function(){
24407         //console.log("INIT EDITOR");
24408         this.assignDocWin();
24409         
24410         
24411         
24412         this.doc.designMode="on";
24413         this.doc.open();
24414         this.doc.write(this.getDocMarkup());
24415         this.doc.close();
24416         
24417         var dbody = (this.doc.body || this.doc.documentElement);
24418         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24419         // this copies styles from the containing element into thsi one..
24420         // not sure why we need all of this..
24421         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24422         
24423         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24424         //ss['background-attachment'] = 'fixed'; // w3c
24425         dbody.bgProperties = 'fixed'; // ie
24426         //Roo.DomHelper.applyStyles(dbody, ss);
24427         Roo.EventManager.on(this.doc, {
24428             //'mousedown': this.onEditorEvent,
24429             'mouseup': this.onEditorEvent,
24430             'dblclick': this.onEditorEvent,
24431             'click': this.onEditorEvent,
24432             'keyup': this.onEditorEvent,
24433             buffer:100,
24434             scope: this
24435         });
24436         if(Roo.isGecko){
24437             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24438         }
24439         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24440             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24441         }
24442         this.initialized = true;
24443
24444         this.owner.fireEvent('initialize', this);
24445         this.pushValue();
24446     },
24447
24448     // private
24449     onDestroy : function(){
24450         
24451         
24452         
24453         if(this.rendered){
24454             
24455             //for (var i =0; i < this.toolbars.length;i++) {
24456             //    // fixme - ask toolbars for heights?
24457             //    this.toolbars[i].onDestroy();
24458            // }
24459             
24460             //this.wrap.dom.innerHTML = '';
24461             //this.wrap.remove();
24462         }
24463     },
24464
24465     // private
24466     onFirstFocus : function(){
24467         
24468         this.assignDocWin();
24469         
24470         
24471         this.activated = true;
24472          
24473     
24474         if(Roo.isGecko){ // prevent silly gecko errors
24475             this.win.focus();
24476             var s = this.win.getSelection();
24477             if(!s.focusNode || s.focusNode.nodeType != 3){
24478                 var r = s.getRangeAt(0);
24479                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24480                 r.collapse(true);
24481                 this.deferFocus();
24482             }
24483             try{
24484                 this.execCmd('useCSS', true);
24485                 this.execCmd('styleWithCSS', false);
24486             }catch(e){}
24487         }
24488         this.owner.fireEvent('activate', this);
24489     },
24490
24491     // private
24492     adjustFont: function(btn){
24493         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24494         //if(Roo.isSafari){ // safari
24495         //    adjust *= 2;
24496        // }
24497         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24498         if(Roo.isSafari){ // safari
24499             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24500             v =  (v < 10) ? 10 : v;
24501             v =  (v > 48) ? 48 : v;
24502             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24503             
24504         }
24505         
24506         
24507         v = Math.max(1, v+adjust);
24508         
24509         this.execCmd('FontSize', v  );
24510     },
24511
24512     onEditorEvent : function(e)
24513     {
24514         this.owner.fireEvent('editorevent', this, e);
24515       //  this.updateToolbar();
24516         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24517     },
24518
24519     insertTag : function(tg)
24520     {
24521         // could be a bit smarter... -> wrap the current selected tRoo..
24522         if (tg.toLowerCase() == 'span' ||
24523             tg.toLowerCase() == 'code' ||
24524             tg.toLowerCase() == 'sup' ||
24525             tg.toLowerCase() == 'sub' 
24526             ) {
24527             
24528             range = this.createRange(this.getSelection());
24529             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24530             wrappingNode.appendChild(range.extractContents());
24531             range.insertNode(wrappingNode);
24532
24533             return;
24534             
24535             
24536             
24537         }
24538         this.execCmd("formatblock",   tg);
24539         
24540     },
24541     
24542     insertText : function(txt)
24543     {
24544         
24545         
24546         var range = this.createRange();
24547         range.deleteContents();
24548                //alert(Sender.getAttribute('label'));
24549                
24550         range.insertNode(this.doc.createTextNode(txt));
24551     } ,
24552     
24553      
24554
24555     /**
24556      * Executes a Midas editor command on the editor document and performs necessary focus and
24557      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24558      * @param {String} cmd The Midas command
24559      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24560      */
24561     relayCmd : function(cmd, value){
24562         this.win.focus();
24563         this.execCmd(cmd, value);
24564         this.owner.fireEvent('editorevent', this);
24565         //this.updateToolbar();
24566         this.owner.deferFocus();
24567     },
24568
24569     /**
24570      * Executes a Midas editor command directly on the editor document.
24571      * For visual commands, you should use {@link #relayCmd} instead.
24572      * <b>This should only be called after the editor is initialized.</b>
24573      * @param {String} cmd The Midas command
24574      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24575      */
24576     execCmd : function(cmd, value){
24577         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24578         this.syncValue();
24579     },
24580  
24581  
24582    
24583     /**
24584      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24585      * to insert tRoo.
24586      * @param {String} text | dom node.. 
24587      */
24588     insertAtCursor : function(text)
24589     {
24590         
24591         if(!this.activated){
24592             return;
24593         }
24594         /*
24595         if(Roo.isIE){
24596             this.win.focus();
24597             var r = this.doc.selection.createRange();
24598             if(r){
24599                 r.collapse(true);
24600                 r.pasteHTML(text);
24601                 this.syncValue();
24602                 this.deferFocus();
24603             
24604             }
24605             return;
24606         }
24607         */
24608         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24609             this.win.focus();
24610             
24611             
24612             // from jquery ui (MIT licenced)
24613             var range, node;
24614             var win = this.win;
24615             
24616             if (win.getSelection && win.getSelection().getRangeAt) {
24617                 range = win.getSelection().getRangeAt(0);
24618                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24619                 range.insertNode(node);
24620             } else if (win.document.selection && win.document.selection.createRange) {
24621                 // no firefox support
24622                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24623                 win.document.selection.createRange().pasteHTML(txt);
24624             } else {
24625                 // no firefox support
24626                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24627                 this.execCmd('InsertHTML', txt);
24628             } 
24629             
24630             this.syncValue();
24631             
24632             this.deferFocus();
24633         }
24634     },
24635  // private
24636     mozKeyPress : function(e){
24637         if(e.ctrlKey){
24638             var c = e.getCharCode(), cmd;
24639           
24640             if(c > 0){
24641                 c = String.fromCharCode(c).toLowerCase();
24642                 switch(c){
24643                     case 'b':
24644                         cmd = 'bold';
24645                         break;
24646                     case 'i':
24647                         cmd = 'italic';
24648                         break;
24649                     
24650                     case 'u':
24651                         cmd = 'underline';
24652                         break;
24653                     
24654                     case 'v':
24655                         this.cleanUpPaste.defer(100, this);
24656                         return;
24657                         
24658                 }
24659                 if(cmd){
24660                     this.win.focus();
24661                     this.execCmd(cmd);
24662                     this.deferFocus();
24663                     e.preventDefault();
24664                 }
24665                 
24666             }
24667         }
24668     },
24669
24670     // private
24671     fixKeys : function(){ // load time branching for fastest keydown performance
24672         if(Roo.isIE){
24673             return function(e){
24674                 var k = e.getKey(), r;
24675                 if(k == e.TAB){
24676                     e.stopEvent();
24677                     r = this.doc.selection.createRange();
24678                     if(r){
24679                         r.collapse(true);
24680                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24681                         this.deferFocus();
24682                     }
24683                     return;
24684                 }
24685                 
24686                 if(k == e.ENTER){
24687                     r = this.doc.selection.createRange();
24688                     if(r){
24689                         var target = r.parentElement();
24690                         if(!target || target.tagName.toLowerCase() != 'li'){
24691                             e.stopEvent();
24692                             r.pasteHTML('<br />');
24693                             r.collapse(false);
24694                             r.select();
24695                         }
24696                     }
24697                 }
24698                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24699                     this.cleanUpPaste.defer(100, this);
24700                     return;
24701                 }
24702                 
24703                 
24704             };
24705         }else if(Roo.isOpera){
24706             return function(e){
24707                 var k = e.getKey();
24708                 if(k == e.TAB){
24709                     e.stopEvent();
24710                     this.win.focus();
24711                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24712                     this.deferFocus();
24713                 }
24714                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24715                     this.cleanUpPaste.defer(100, this);
24716                     return;
24717                 }
24718                 
24719             };
24720         }else if(Roo.isSafari){
24721             return function(e){
24722                 var k = e.getKey();
24723                 
24724                 if(k == e.TAB){
24725                     e.stopEvent();
24726                     this.execCmd('InsertText','\t');
24727                     this.deferFocus();
24728                     return;
24729                 }
24730                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24731                     this.cleanUpPaste.defer(100, this);
24732                     return;
24733                 }
24734                 
24735              };
24736         }
24737     }(),
24738     
24739     getAllAncestors: function()
24740     {
24741         var p = this.getSelectedNode();
24742         var a = [];
24743         if (!p) {
24744             a.push(p); // push blank onto stack..
24745             p = this.getParentElement();
24746         }
24747         
24748         
24749         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24750             a.push(p);
24751             p = p.parentNode;
24752         }
24753         a.push(this.doc.body);
24754         return a;
24755     },
24756     lastSel : false,
24757     lastSelNode : false,
24758     
24759     
24760     getSelection : function() 
24761     {
24762         this.assignDocWin();
24763         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24764     },
24765     
24766     getSelectedNode: function() 
24767     {
24768         // this may only work on Gecko!!!
24769         
24770         // should we cache this!!!!
24771         
24772         
24773         
24774          
24775         var range = this.createRange(this.getSelection()).cloneRange();
24776         
24777         if (Roo.isIE) {
24778             var parent = range.parentElement();
24779             while (true) {
24780                 var testRange = range.duplicate();
24781                 testRange.moveToElementText(parent);
24782                 if (testRange.inRange(range)) {
24783                     break;
24784                 }
24785                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24786                     break;
24787                 }
24788                 parent = parent.parentElement;
24789             }
24790             return parent;
24791         }
24792         
24793         // is ancestor a text element.
24794         var ac =  range.commonAncestorContainer;
24795         if (ac.nodeType == 3) {
24796             ac = ac.parentNode;
24797         }
24798         
24799         var ar = ac.childNodes;
24800          
24801         var nodes = [];
24802         var other_nodes = [];
24803         var has_other_nodes = false;
24804         for (var i=0;i<ar.length;i++) {
24805             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24806                 continue;
24807             }
24808             // fullly contained node.
24809             
24810             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24811                 nodes.push(ar[i]);
24812                 continue;
24813             }
24814             
24815             // probably selected..
24816             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24817                 other_nodes.push(ar[i]);
24818                 continue;
24819             }
24820             // outer..
24821             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24822                 continue;
24823             }
24824             
24825             
24826             has_other_nodes = true;
24827         }
24828         if (!nodes.length && other_nodes.length) {
24829             nodes= other_nodes;
24830         }
24831         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24832             return false;
24833         }
24834         
24835         return nodes[0];
24836     },
24837     createRange: function(sel)
24838     {
24839         // this has strange effects when using with 
24840         // top toolbar - not sure if it's a great idea.
24841         //this.editor.contentWindow.focus();
24842         if (typeof sel != "undefined") {
24843             try {
24844                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24845             } catch(e) {
24846                 return this.doc.createRange();
24847             }
24848         } else {
24849             return this.doc.createRange();
24850         }
24851     },
24852     getParentElement: function()
24853     {
24854         
24855         this.assignDocWin();
24856         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24857         
24858         var range = this.createRange(sel);
24859          
24860         try {
24861             var p = range.commonAncestorContainer;
24862             while (p.nodeType == 3) { // text node
24863                 p = p.parentNode;
24864             }
24865             return p;
24866         } catch (e) {
24867             return null;
24868         }
24869     
24870     },
24871     /***
24872      *
24873      * Range intersection.. the hard stuff...
24874      *  '-1' = before
24875      *  '0' = hits..
24876      *  '1' = after.
24877      *         [ -- selected range --- ]
24878      *   [fail]                        [fail]
24879      *
24880      *    basically..
24881      *      if end is before start or  hits it. fail.
24882      *      if start is after end or hits it fail.
24883      *
24884      *   if either hits (but other is outside. - then it's not 
24885      *   
24886      *    
24887      **/
24888     
24889     
24890     // @see http://www.thismuchiknow.co.uk/?p=64.
24891     rangeIntersectsNode : function(range, node)
24892     {
24893         var nodeRange = node.ownerDocument.createRange();
24894         try {
24895             nodeRange.selectNode(node);
24896         } catch (e) {
24897             nodeRange.selectNodeContents(node);
24898         }
24899     
24900         var rangeStartRange = range.cloneRange();
24901         rangeStartRange.collapse(true);
24902     
24903         var rangeEndRange = range.cloneRange();
24904         rangeEndRange.collapse(false);
24905     
24906         var nodeStartRange = nodeRange.cloneRange();
24907         nodeStartRange.collapse(true);
24908     
24909         var nodeEndRange = nodeRange.cloneRange();
24910         nodeEndRange.collapse(false);
24911     
24912         return rangeStartRange.compareBoundaryPoints(
24913                  Range.START_TO_START, nodeEndRange) == -1 &&
24914                rangeEndRange.compareBoundaryPoints(
24915                  Range.START_TO_START, nodeStartRange) == 1;
24916         
24917          
24918     },
24919     rangeCompareNode : function(range, node)
24920     {
24921         var nodeRange = node.ownerDocument.createRange();
24922         try {
24923             nodeRange.selectNode(node);
24924         } catch (e) {
24925             nodeRange.selectNodeContents(node);
24926         }
24927         
24928         
24929         range.collapse(true);
24930     
24931         nodeRange.collapse(true);
24932      
24933         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24934         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24935          
24936         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24937         
24938         var nodeIsBefore   =  ss == 1;
24939         var nodeIsAfter    = ee == -1;
24940         
24941         if (nodeIsBefore && nodeIsAfter) {
24942             return 0; // outer
24943         }
24944         if (!nodeIsBefore && nodeIsAfter) {
24945             return 1; //right trailed.
24946         }
24947         
24948         if (nodeIsBefore && !nodeIsAfter) {
24949             return 2;  // left trailed.
24950         }
24951         // fully contined.
24952         return 3;
24953     },
24954
24955     // private? - in a new class?
24956     cleanUpPaste :  function()
24957     {
24958         // cleans up the whole document..
24959         Roo.log('cleanuppaste');
24960         
24961         this.cleanUpChildren(this.doc.body);
24962         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24963         if (clean != this.doc.body.innerHTML) {
24964             this.doc.body.innerHTML = clean;
24965         }
24966         
24967     },
24968     
24969     cleanWordChars : function(input) {// change the chars to hex code
24970         var he = Roo.HtmlEditorCore;
24971         
24972         var output = input;
24973         Roo.each(he.swapCodes, function(sw) { 
24974             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24975             
24976             output = output.replace(swapper, sw[1]);
24977         });
24978         
24979         return output;
24980     },
24981     
24982     
24983     cleanUpChildren : function (n)
24984     {
24985         if (!n.childNodes.length) {
24986             return;
24987         }
24988         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24989            this.cleanUpChild(n.childNodes[i]);
24990         }
24991     },
24992     
24993     
24994         
24995     
24996     cleanUpChild : function (node)
24997     {
24998         var ed = this;
24999         //console.log(node);
25000         if (node.nodeName == "#text") {
25001             // clean up silly Windows -- stuff?
25002             return; 
25003         }
25004         if (node.nodeName == "#comment") {
25005             node.parentNode.removeChild(node);
25006             // clean up silly Windows -- stuff?
25007             return; 
25008         }
25009         var lcname = node.tagName.toLowerCase();
25010         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25011         // whitelist of tags..
25012         
25013         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25014             // remove node.
25015             node.parentNode.removeChild(node);
25016             return;
25017             
25018         }
25019         
25020         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25021         
25022         // spans with no attributes - just remove them..
25023         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25024             remove_keep_children = true;
25025         }
25026         
25027         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25028         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25029         
25030         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25031         //    remove_keep_children = true;
25032         //}
25033         
25034         if (remove_keep_children) {
25035             this.cleanUpChildren(node);
25036             // inserts everything just before this node...
25037             while (node.childNodes.length) {
25038                 var cn = node.childNodes[0];
25039                 node.removeChild(cn);
25040                 node.parentNode.insertBefore(cn, node);
25041             }
25042             node.parentNode.removeChild(node);
25043             return;
25044         }
25045         
25046         if (!node.attributes || !node.attributes.length) {
25047             
25048           
25049             
25050             
25051             this.cleanUpChildren(node);
25052             return;
25053         }
25054         
25055         function cleanAttr(n,v)
25056         {
25057             
25058             if (v.match(/^\./) || v.match(/^\//)) {
25059                 return;
25060             }
25061             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25062                 return;
25063             }
25064             if (v.match(/^#/)) {
25065                 return;
25066             }
25067             if (v.match(/^\{/)) { // allow template editing.
25068                 return;
25069             }
25070 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25071             node.removeAttribute(n);
25072             
25073         }
25074         
25075         var cwhite = this.cwhite;
25076         var cblack = this.cblack;
25077             
25078         function cleanStyle(n,v)
25079         {
25080             if (v.match(/expression/)) { //XSS?? should we even bother..
25081                 node.removeAttribute(n);
25082                 return;
25083             }
25084             
25085             var parts = v.split(/;/);
25086             var clean = [];
25087             
25088             Roo.each(parts, function(p) {
25089                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25090                 if (!p.length) {
25091                     return true;
25092                 }
25093                 var l = p.split(':').shift().replace(/\s+/g,'');
25094                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25095                 
25096                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25097 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25098                     //node.removeAttribute(n);
25099                     return true;
25100                 }
25101                 //Roo.log()
25102                 // only allow 'c whitelisted system attributes'
25103                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25104 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25105                     //node.removeAttribute(n);
25106                     return true;
25107                 }
25108                 
25109                 
25110                  
25111                 
25112                 clean.push(p);
25113                 return true;
25114             });
25115             if (clean.length) { 
25116                 node.setAttribute(n, clean.join(';'));
25117             } else {
25118                 node.removeAttribute(n);
25119             }
25120             
25121         }
25122         
25123         
25124         for (var i = node.attributes.length-1; i > -1 ; i--) {
25125             var a = node.attributes[i];
25126             //console.log(a);
25127             
25128             if (a.name.toLowerCase().substr(0,2)=='on')  {
25129                 node.removeAttribute(a.name);
25130                 continue;
25131             }
25132             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25133                 node.removeAttribute(a.name);
25134                 continue;
25135             }
25136             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25137                 cleanAttr(a.name,a.value); // fixme..
25138                 continue;
25139             }
25140             if (a.name == 'style') {
25141                 cleanStyle(a.name,a.value);
25142                 continue;
25143             }
25144             /// clean up MS crap..
25145             // tecnically this should be a list of valid class'es..
25146             
25147             
25148             if (a.name == 'class') {
25149                 if (a.value.match(/^Mso/)) {
25150                     node.removeAttribute('class');
25151                 }
25152                 
25153                 if (a.value.match(/^body$/)) {
25154                     node.removeAttribute('class');
25155                 }
25156                 continue;
25157             }
25158             
25159             // style cleanup!?
25160             // class cleanup?
25161             
25162         }
25163         
25164         
25165         this.cleanUpChildren(node);
25166         
25167         
25168     },
25169     
25170     /**
25171      * Clean up MS wordisms...
25172      */
25173     cleanWord : function(node)
25174     {
25175         if (!node) {
25176             this.cleanWord(this.doc.body);
25177             return;
25178         }
25179         
25180         if(
25181                 node.nodeName == 'SPAN' &&
25182                 !node.hasAttributes() &&
25183                 node.childNodes.length == 1 &&
25184                 node.firstChild.nodeName == "#text"  
25185         ) {
25186             var textNode = node.firstChild;
25187             node.removeChild(textNode);
25188             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25189                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25190             }
25191             node.parentNode.insertBefore(textNode, node);
25192             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25193                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25194             }
25195             node.parentNode.removeChild(node);
25196         }
25197         
25198         if (node.nodeName == "#text") {
25199             // clean up silly Windows -- stuff?
25200             return; 
25201         }
25202         if (node.nodeName == "#comment") {
25203             node.parentNode.removeChild(node);
25204             // clean up silly Windows -- stuff?
25205             return; 
25206         }
25207         
25208         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25209             node.parentNode.removeChild(node);
25210             return;
25211         }
25212         //Roo.log(node.tagName);
25213         // remove - but keep children..
25214         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25215             //Roo.log('-- removed');
25216             while (node.childNodes.length) {
25217                 var cn = node.childNodes[0];
25218                 node.removeChild(cn);
25219                 node.parentNode.insertBefore(cn, node);
25220                 // move node to parent - and clean it..
25221                 this.cleanWord(cn);
25222             }
25223             node.parentNode.removeChild(node);
25224             /// no need to iterate chidlren = it's got none..
25225             //this.iterateChildren(node, this.cleanWord);
25226             return;
25227         }
25228         // clean styles
25229         if (node.className.length) {
25230             
25231             var cn = node.className.split(/\W+/);
25232             var cna = [];
25233             Roo.each(cn, function(cls) {
25234                 if (cls.match(/Mso[a-zA-Z]+/)) {
25235                     return;
25236                 }
25237                 cna.push(cls);
25238             });
25239             node.className = cna.length ? cna.join(' ') : '';
25240             if (!cna.length) {
25241                 node.removeAttribute("class");
25242             }
25243         }
25244         
25245         if (node.hasAttribute("lang")) {
25246             node.removeAttribute("lang");
25247         }
25248         
25249         if (node.hasAttribute("style")) {
25250             
25251             var styles = node.getAttribute("style").split(";");
25252             var nstyle = [];
25253             Roo.each(styles, function(s) {
25254                 if (!s.match(/:/)) {
25255                     return;
25256                 }
25257                 var kv = s.split(":");
25258                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25259                     return;
25260                 }
25261                 // what ever is left... we allow.
25262                 nstyle.push(s);
25263             });
25264             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25265             if (!nstyle.length) {
25266                 node.removeAttribute('style');
25267             }
25268         }
25269         this.iterateChildren(node, this.cleanWord);
25270         
25271         
25272         
25273     },
25274     /**
25275      * iterateChildren of a Node, calling fn each time, using this as the scole..
25276      * @param {DomNode} node node to iterate children of.
25277      * @param {Function} fn method of this class to call on each item.
25278      */
25279     iterateChildren : function(node, fn)
25280     {
25281         if (!node.childNodes.length) {
25282                 return;
25283         }
25284         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25285            fn.call(this, node.childNodes[i])
25286         }
25287     },
25288     
25289     
25290     /**
25291      * cleanTableWidths.
25292      *
25293      * Quite often pasting from word etc.. results in tables with column and widths.
25294      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25295      *
25296      */
25297     cleanTableWidths : function(node)
25298     {
25299          
25300          
25301         if (!node) {
25302             this.cleanTableWidths(this.doc.body);
25303             return;
25304         }
25305         
25306         // ignore list...
25307         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25308             return; 
25309         }
25310         Roo.log(node.tagName);
25311         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25312             this.iterateChildren(node, this.cleanTableWidths);
25313             return;
25314         }
25315         if (node.hasAttribute('width')) {
25316             node.removeAttribute('width');
25317         }
25318         
25319          
25320         if (node.hasAttribute("style")) {
25321             // pretty basic...
25322             
25323             var styles = node.getAttribute("style").split(";");
25324             var nstyle = [];
25325             Roo.each(styles, function(s) {
25326                 if (!s.match(/:/)) {
25327                     return;
25328                 }
25329                 var kv = s.split(":");
25330                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25331                     return;
25332                 }
25333                 // what ever is left... we allow.
25334                 nstyle.push(s);
25335             });
25336             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25337             if (!nstyle.length) {
25338                 node.removeAttribute('style');
25339             }
25340         }
25341         
25342         this.iterateChildren(node, this.cleanTableWidths);
25343         
25344         
25345     },
25346     
25347     
25348     
25349     
25350     domToHTML : function(currentElement, depth, nopadtext) {
25351         
25352         depth = depth || 0;
25353         nopadtext = nopadtext || false;
25354     
25355         if (!currentElement) {
25356             return this.domToHTML(this.doc.body);
25357         }
25358         
25359         //Roo.log(currentElement);
25360         var j;
25361         var allText = false;
25362         var nodeName = currentElement.nodeName;
25363         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25364         
25365         if  (nodeName == '#text') {
25366             
25367             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25368         }
25369         
25370         
25371         var ret = '';
25372         if (nodeName != 'BODY') {
25373              
25374             var i = 0;
25375             // Prints the node tagName, such as <A>, <IMG>, etc
25376             if (tagName) {
25377                 var attr = [];
25378                 for(i = 0; i < currentElement.attributes.length;i++) {
25379                     // quoting?
25380                     var aname = currentElement.attributes.item(i).name;
25381                     if (!currentElement.attributes.item(i).value.length) {
25382                         continue;
25383                     }
25384                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25385                 }
25386                 
25387                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25388             } 
25389             else {
25390                 
25391                 // eack
25392             }
25393         } else {
25394             tagName = false;
25395         }
25396         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25397             return ret;
25398         }
25399         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25400             nopadtext = true;
25401         }
25402         
25403         
25404         // Traverse the tree
25405         i = 0;
25406         var currentElementChild = currentElement.childNodes.item(i);
25407         var allText = true;
25408         var innerHTML  = '';
25409         lastnode = '';
25410         while (currentElementChild) {
25411             // Formatting code (indent the tree so it looks nice on the screen)
25412             var nopad = nopadtext;
25413             if (lastnode == 'SPAN') {
25414                 nopad  = true;
25415             }
25416             // text
25417             if  (currentElementChild.nodeName == '#text') {
25418                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25419                 toadd = nopadtext ? toadd : toadd.trim();
25420                 if (!nopad && toadd.length > 80) {
25421                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25422                 }
25423                 innerHTML  += toadd;
25424                 
25425                 i++;
25426                 currentElementChild = currentElement.childNodes.item(i);
25427                 lastNode = '';
25428                 continue;
25429             }
25430             allText = false;
25431             
25432             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25433                 
25434             // Recursively traverse the tree structure of the child node
25435             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25436             lastnode = currentElementChild.nodeName;
25437             i++;
25438             currentElementChild=currentElement.childNodes.item(i);
25439         }
25440         
25441         ret += innerHTML;
25442         
25443         if (!allText) {
25444                 // The remaining code is mostly for formatting the tree
25445             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25446         }
25447         
25448         
25449         if (tagName) {
25450             ret+= "</"+tagName+">";
25451         }
25452         return ret;
25453         
25454     },
25455         
25456     applyBlacklists : function()
25457     {
25458         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25459         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25460         
25461         this.white = [];
25462         this.black = [];
25463         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25464             if (b.indexOf(tag) > -1) {
25465                 return;
25466             }
25467             this.white.push(tag);
25468             
25469         }, this);
25470         
25471         Roo.each(w, function(tag) {
25472             if (b.indexOf(tag) > -1) {
25473                 return;
25474             }
25475             if (this.white.indexOf(tag) > -1) {
25476                 return;
25477             }
25478             this.white.push(tag);
25479             
25480         }, this);
25481         
25482         
25483         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25484             if (w.indexOf(tag) > -1) {
25485                 return;
25486             }
25487             this.black.push(tag);
25488             
25489         }, this);
25490         
25491         Roo.each(b, function(tag) {
25492             if (w.indexOf(tag) > -1) {
25493                 return;
25494             }
25495             if (this.black.indexOf(tag) > -1) {
25496                 return;
25497             }
25498             this.black.push(tag);
25499             
25500         }, this);
25501         
25502         
25503         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25504         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25505         
25506         this.cwhite = [];
25507         this.cblack = [];
25508         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25509             if (b.indexOf(tag) > -1) {
25510                 return;
25511             }
25512             this.cwhite.push(tag);
25513             
25514         }, this);
25515         
25516         Roo.each(w, function(tag) {
25517             if (b.indexOf(tag) > -1) {
25518                 return;
25519             }
25520             if (this.cwhite.indexOf(tag) > -1) {
25521                 return;
25522             }
25523             this.cwhite.push(tag);
25524             
25525         }, this);
25526         
25527         
25528         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25529             if (w.indexOf(tag) > -1) {
25530                 return;
25531             }
25532             this.cblack.push(tag);
25533             
25534         }, this);
25535         
25536         Roo.each(b, function(tag) {
25537             if (w.indexOf(tag) > -1) {
25538                 return;
25539             }
25540             if (this.cblack.indexOf(tag) > -1) {
25541                 return;
25542             }
25543             this.cblack.push(tag);
25544             
25545         }, this);
25546     },
25547     
25548     setStylesheets : function(stylesheets)
25549     {
25550         if(typeof(stylesheets) == 'string'){
25551             Roo.get(this.iframe.contentDocument.head).createChild({
25552                 tag : 'link',
25553                 rel : 'stylesheet',
25554                 type : 'text/css',
25555                 href : stylesheets
25556             });
25557             
25558             return;
25559         }
25560         var _this = this;
25561      
25562         Roo.each(stylesheets, function(s) {
25563             if(!s.length){
25564                 return;
25565             }
25566             
25567             Roo.get(_this.iframe.contentDocument.head).createChild({
25568                 tag : 'link',
25569                 rel : 'stylesheet',
25570                 type : 'text/css',
25571                 href : s
25572             });
25573         });
25574
25575         
25576     },
25577     
25578     removeStylesheets : function()
25579     {
25580         var _this = this;
25581         
25582         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25583             s.remove();
25584         });
25585     },
25586     
25587     setStyle : function(style)
25588     {
25589         Roo.get(this.iframe.contentDocument.head).createChild({
25590             tag : 'style',
25591             type : 'text/css',
25592             html : style
25593         });
25594
25595         return;
25596     }
25597     
25598     // hide stuff that is not compatible
25599     /**
25600      * @event blur
25601      * @hide
25602      */
25603     /**
25604      * @event change
25605      * @hide
25606      */
25607     /**
25608      * @event focus
25609      * @hide
25610      */
25611     /**
25612      * @event specialkey
25613      * @hide
25614      */
25615     /**
25616      * @cfg {String} fieldClass @hide
25617      */
25618     /**
25619      * @cfg {String} focusClass @hide
25620      */
25621     /**
25622      * @cfg {String} autoCreate @hide
25623      */
25624     /**
25625      * @cfg {String} inputType @hide
25626      */
25627     /**
25628      * @cfg {String} invalidClass @hide
25629      */
25630     /**
25631      * @cfg {String} invalidText @hide
25632      */
25633     /**
25634      * @cfg {String} msgFx @hide
25635      */
25636     /**
25637      * @cfg {String} validateOnBlur @hide
25638      */
25639 });
25640
25641 Roo.HtmlEditorCore.white = [
25642         'area', 'br', 'img', 'input', 'hr', 'wbr',
25643         
25644        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25645        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25646        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25647        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25648        'table',   'ul',         'xmp', 
25649        
25650        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25651       'thead',   'tr', 
25652      
25653       'dir', 'menu', 'ol', 'ul', 'dl',
25654        
25655       'embed',  'object'
25656 ];
25657
25658
25659 Roo.HtmlEditorCore.black = [
25660     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25661         'applet', // 
25662         'base',   'basefont', 'bgsound', 'blink',  'body', 
25663         'frame',  'frameset', 'head',    'html',   'ilayer', 
25664         'iframe', 'layer',  'link',     'meta',    'object',   
25665         'script', 'style' ,'title',  'xml' // clean later..
25666 ];
25667 Roo.HtmlEditorCore.clean = [
25668     'script', 'style', 'title', 'xml'
25669 ];
25670 Roo.HtmlEditorCore.remove = [
25671     'font'
25672 ];
25673 // attributes..
25674
25675 Roo.HtmlEditorCore.ablack = [
25676     'on'
25677 ];
25678     
25679 Roo.HtmlEditorCore.aclean = [ 
25680     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25681 ];
25682
25683 // protocols..
25684 Roo.HtmlEditorCore.pwhite= [
25685         'http',  'https',  'mailto'
25686 ];
25687
25688 // white listed style attributes.
25689 Roo.HtmlEditorCore.cwhite= [
25690       //  'text-align', /// default is to allow most things..
25691       
25692          
25693 //        'font-size'//??
25694 ];
25695
25696 // black listed style attributes.
25697 Roo.HtmlEditorCore.cblack= [
25698       //  'font-size' -- this can be set by the project 
25699 ];
25700
25701
25702 Roo.HtmlEditorCore.swapCodes   =[ 
25703     [    8211, "--" ], 
25704     [    8212, "--" ], 
25705     [    8216,  "'" ],  
25706     [    8217, "'" ],  
25707     [    8220, '"' ],  
25708     [    8221, '"' ],  
25709     [    8226, "*" ],  
25710     [    8230, "..." ]
25711 ]; 
25712
25713     /*
25714  * - LGPL
25715  *
25716  * HtmlEditor
25717  * 
25718  */
25719
25720 /**
25721  * @class Roo.bootstrap.HtmlEditor
25722  * @extends Roo.bootstrap.TextArea
25723  * Bootstrap HtmlEditor class
25724
25725  * @constructor
25726  * Create a new HtmlEditor
25727  * @param {Object} config The config object
25728  */
25729
25730 Roo.bootstrap.HtmlEditor = function(config){
25731     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25732     if (!this.toolbars) {
25733         this.toolbars = [];
25734     }
25735     
25736     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25737     this.addEvents({
25738             /**
25739              * @event initialize
25740              * Fires when the editor is fully initialized (including the iframe)
25741              * @param {HtmlEditor} this
25742              */
25743             initialize: true,
25744             /**
25745              * @event activate
25746              * Fires when the editor is first receives the focus. Any insertion must wait
25747              * until after this event.
25748              * @param {HtmlEditor} this
25749              */
25750             activate: true,
25751              /**
25752              * @event beforesync
25753              * Fires before the textarea is updated with content from the editor iframe. Return false
25754              * to cancel the sync.
25755              * @param {HtmlEditor} this
25756              * @param {String} html
25757              */
25758             beforesync: true,
25759              /**
25760              * @event beforepush
25761              * Fires before the iframe editor is updated with content from the textarea. Return false
25762              * to cancel the push.
25763              * @param {HtmlEditor} this
25764              * @param {String} html
25765              */
25766             beforepush: true,
25767              /**
25768              * @event sync
25769              * Fires when the textarea is updated with content from the editor iframe.
25770              * @param {HtmlEditor} this
25771              * @param {String} html
25772              */
25773             sync: true,
25774              /**
25775              * @event push
25776              * Fires when the iframe editor is updated with content from the textarea.
25777              * @param {HtmlEditor} this
25778              * @param {String} html
25779              */
25780             push: true,
25781              /**
25782              * @event editmodechange
25783              * Fires when the editor switches edit modes
25784              * @param {HtmlEditor} this
25785              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25786              */
25787             editmodechange: true,
25788             /**
25789              * @event editorevent
25790              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25791              * @param {HtmlEditor} this
25792              */
25793             editorevent: true,
25794             /**
25795              * @event firstfocus
25796              * Fires when on first focus - needed by toolbars..
25797              * @param {HtmlEditor} this
25798              */
25799             firstfocus: true,
25800             /**
25801              * @event autosave
25802              * Auto save the htmlEditor value as a file into Events
25803              * @param {HtmlEditor} this
25804              */
25805             autosave: true,
25806             /**
25807              * @event savedpreview
25808              * preview the saved version of htmlEditor
25809              * @param {HtmlEditor} this
25810              */
25811             savedpreview: true
25812         });
25813 };
25814
25815
25816 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25817     
25818     
25819       /**
25820      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25821      */
25822     toolbars : false,
25823     
25824      /**
25825     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25826     */
25827     btns : [],
25828    
25829      /**
25830      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25831      *                        Roo.resizable.
25832      */
25833     resizable : false,
25834      /**
25835      * @cfg {Number} height (in pixels)
25836      */   
25837     height: 300,
25838    /**
25839      * @cfg {Number} width (in pixels)
25840      */   
25841     width: false,
25842     
25843     /**
25844      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25845      * 
25846      */
25847     stylesheets: false,
25848     
25849     // id of frame..
25850     frameId: false,
25851     
25852     // private properties
25853     validationEvent : false,
25854     deferHeight: true,
25855     initialized : false,
25856     activated : false,
25857     
25858     onFocus : Roo.emptyFn,
25859     iframePad:3,
25860     hideMode:'offsets',
25861     
25862     tbContainer : false,
25863     
25864     bodyCls : '',
25865     
25866     toolbarContainer :function() {
25867         return this.wrap.select('.x-html-editor-tb',true).first();
25868     },
25869
25870     /**
25871      * Protected method that will not generally be called directly. It
25872      * is called when the editor creates its toolbar. Override this method if you need to
25873      * add custom toolbar buttons.
25874      * @param {HtmlEditor} editor
25875      */
25876     createToolbar : function(){
25877         Roo.log('renewing');
25878         Roo.log("create toolbars");
25879         
25880         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25881         this.toolbars[0].render(this.toolbarContainer());
25882         
25883         return;
25884         
25885 //        if (!editor.toolbars || !editor.toolbars.length) {
25886 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25887 //        }
25888 //        
25889 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25890 //            editor.toolbars[i] = Roo.factory(
25891 //                    typeof(editor.toolbars[i]) == 'string' ?
25892 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25893 //                Roo.bootstrap.HtmlEditor);
25894 //            editor.toolbars[i].init(editor);
25895 //        }
25896     },
25897
25898      
25899     // private
25900     onRender : function(ct, position)
25901     {
25902        // Roo.log("Call onRender: " + this.xtype);
25903         var _t = this;
25904         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25905       
25906         this.wrap = this.inputEl().wrap({
25907             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25908         });
25909         
25910         this.editorcore.onRender(ct, position);
25911          
25912         if (this.resizable) {
25913             this.resizeEl = new Roo.Resizable(this.wrap, {
25914                 pinned : true,
25915                 wrap: true,
25916                 dynamic : true,
25917                 minHeight : this.height,
25918                 height: this.height,
25919                 handles : this.resizable,
25920                 width: this.width,
25921                 listeners : {
25922                     resize : function(r, w, h) {
25923                         _t.onResize(w,h); // -something
25924                     }
25925                 }
25926             });
25927             
25928         }
25929         this.createToolbar(this);
25930        
25931         
25932         if(!this.width && this.resizable){
25933             this.setSize(this.wrap.getSize());
25934         }
25935         if (this.resizeEl) {
25936             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25937             // should trigger onReize..
25938         }
25939         
25940     },
25941
25942     // private
25943     onResize : function(w, h)
25944     {
25945         Roo.log('resize: ' +w + ',' + h );
25946         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25947         var ew = false;
25948         var eh = false;
25949         
25950         if(this.inputEl() ){
25951             if(typeof w == 'number'){
25952                 var aw = w - this.wrap.getFrameWidth('lr');
25953                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25954                 ew = aw;
25955             }
25956             if(typeof h == 'number'){
25957                  var tbh = -11;  // fixme it needs to tool bar size!
25958                 for (var i =0; i < this.toolbars.length;i++) {
25959                     // fixme - ask toolbars for heights?
25960                     tbh += this.toolbars[i].el.getHeight();
25961                     //if (this.toolbars[i].footer) {
25962                     //    tbh += this.toolbars[i].footer.el.getHeight();
25963                     //}
25964                 }
25965               
25966                 
25967                 
25968                 
25969                 
25970                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25971                 ah -= 5; // knock a few pixes off for look..
25972                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25973                 var eh = ah;
25974             }
25975         }
25976         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25977         this.editorcore.onResize(ew,eh);
25978         
25979     },
25980
25981     /**
25982      * Toggles the editor between standard and source edit mode.
25983      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25984      */
25985     toggleSourceEdit : function(sourceEditMode)
25986     {
25987         this.editorcore.toggleSourceEdit(sourceEditMode);
25988         
25989         if(this.editorcore.sourceEditMode){
25990             Roo.log('editor - showing textarea');
25991             
25992 //            Roo.log('in');
25993 //            Roo.log(this.syncValue());
25994             this.syncValue();
25995             this.inputEl().removeClass(['hide', 'x-hidden']);
25996             this.inputEl().dom.removeAttribute('tabIndex');
25997             this.inputEl().focus();
25998         }else{
25999             Roo.log('editor - hiding textarea');
26000 //            Roo.log('out')
26001 //            Roo.log(this.pushValue()); 
26002             this.pushValue();
26003             
26004             this.inputEl().addClass(['hide', 'x-hidden']);
26005             this.inputEl().dom.setAttribute('tabIndex', -1);
26006             //this.deferFocus();
26007         }
26008          
26009         if(this.resizable){
26010             this.setSize(this.wrap.getSize());
26011         }
26012         
26013         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26014     },
26015  
26016     // private (for BoxComponent)
26017     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26018
26019     // private (for BoxComponent)
26020     getResizeEl : function(){
26021         return this.wrap;
26022     },
26023
26024     // private (for BoxComponent)
26025     getPositionEl : function(){
26026         return this.wrap;
26027     },
26028
26029     // private
26030     initEvents : function(){
26031         this.originalValue = this.getValue();
26032     },
26033
26034 //    /**
26035 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26036 //     * @method
26037 //     */
26038 //    markInvalid : Roo.emptyFn,
26039 //    /**
26040 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26041 //     * @method
26042 //     */
26043 //    clearInvalid : Roo.emptyFn,
26044
26045     setValue : function(v){
26046         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26047         this.editorcore.pushValue();
26048     },
26049
26050      
26051     // private
26052     deferFocus : function(){
26053         this.focus.defer(10, this);
26054     },
26055
26056     // doc'ed in Field
26057     focus : function(){
26058         this.editorcore.focus();
26059         
26060     },
26061       
26062
26063     // private
26064     onDestroy : function(){
26065         
26066         
26067         
26068         if(this.rendered){
26069             
26070             for (var i =0; i < this.toolbars.length;i++) {
26071                 // fixme - ask toolbars for heights?
26072                 this.toolbars[i].onDestroy();
26073             }
26074             
26075             this.wrap.dom.innerHTML = '';
26076             this.wrap.remove();
26077         }
26078     },
26079
26080     // private
26081     onFirstFocus : function(){
26082         //Roo.log("onFirstFocus");
26083         this.editorcore.onFirstFocus();
26084          for (var i =0; i < this.toolbars.length;i++) {
26085             this.toolbars[i].onFirstFocus();
26086         }
26087         
26088     },
26089     
26090     // private
26091     syncValue : function()
26092     {   
26093         this.editorcore.syncValue();
26094     },
26095     
26096     pushValue : function()
26097     {   
26098         this.editorcore.pushValue();
26099     }
26100      
26101     
26102     // hide stuff that is not compatible
26103     /**
26104      * @event blur
26105      * @hide
26106      */
26107     /**
26108      * @event change
26109      * @hide
26110      */
26111     /**
26112      * @event focus
26113      * @hide
26114      */
26115     /**
26116      * @event specialkey
26117      * @hide
26118      */
26119     /**
26120      * @cfg {String} fieldClass @hide
26121      */
26122     /**
26123      * @cfg {String} focusClass @hide
26124      */
26125     /**
26126      * @cfg {String} autoCreate @hide
26127      */
26128     /**
26129      * @cfg {String} inputType @hide
26130      */
26131      
26132     /**
26133      * @cfg {String} invalidText @hide
26134      */
26135     /**
26136      * @cfg {String} msgFx @hide
26137      */
26138     /**
26139      * @cfg {String} validateOnBlur @hide
26140      */
26141 });
26142  
26143     
26144    
26145    
26146    
26147       
26148 Roo.namespace('Roo.bootstrap.htmleditor');
26149 /**
26150  * @class Roo.bootstrap.HtmlEditorToolbar1
26151  * Basic Toolbar
26152  * 
26153  * @example
26154  * Usage:
26155  *
26156  new Roo.bootstrap.HtmlEditor({
26157     ....
26158     toolbars : [
26159         new Roo.bootstrap.HtmlEditorToolbar1({
26160             disable : { fonts: 1 , format: 1, ..., ... , ...],
26161             btns : [ .... ]
26162         })
26163     }
26164      
26165  * 
26166  * @cfg {Object} disable List of elements to disable..
26167  * @cfg {Array} btns List of additional buttons.
26168  * 
26169  * 
26170  * NEEDS Extra CSS? 
26171  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26172  */
26173  
26174 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26175 {
26176     
26177     Roo.apply(this, config);
26178     
26179     // default disabled, based on 'good practice'..
26180     this.disable = this.disable || {};
26181     Roo.applyIf(this.disable, {
26182         fontSize : true,
26183         colors : true,
26184         specialElements : true
26185     });
26186     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26187     
26188     this.editor = config.editor;
26189     this.editorcore = config.editor.editorcore;
26190     
26191     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26192     
26193     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26194     // dont call parent... till later.
26195 }
26196 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26197      
26198     bar : true,
26199     
26200     editor : false,
26201     editorcore : false,
26202     
26203     
26204     formats : [
26205         "p" ,  
26206         "h1","h2","h3","h4","h5","h6", 
26207         "pre", "code", 
26208         "abbr", "acronym", "address", "cite", "samp", "var",
26209         'div','span'
26210     ],
26211     
26212     onRender : function(ct, position)
26213     {
26214        // Roo.log("Call onRender: " + this.xtype);
26215         
26216        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26217        Roo.log(this.el);
26218        this.el.dom.style.marginBottom = '0';
26219        var _this = this;
26220        var editorcore = this.editorcore;
26221        var editor= this.editor;
26222        
26223        var children = [];
26224        var btn = function(id,cmd , toggle, handler, html){
26225        
26226             var  event = toggle ? 'toggle' : 'click';
26227        
26228             var a = {
26229                 size : 'sm',
26230                 xtype: 'Button',
26231                 xns: Roo.bootstrap,
26232                 //glyphicon : id,
26233                 fa: id,
26234                 cmd : id || cmd,
26235                 enableToggle:toggle !== false,
26236                 html : html || '',
26237                 pressed : toggle ? false : null,
26238                 listeners : {}
26239             };
26240             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26241                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26242             };
26243             children.push(a);
26244             return a;
26245        }
26246        
26247     //    var cb_box = function...
26248         
26249         var style = {
26250                 xtype: 'Button',
26251                 size : 'sm',
26252                 xns: Roo.bootstrap,
26253                 fa : 'font',
26254                 //html : 'submit'
26255                 menu : {
26256                     xtype: 'Menu',
26257                     xns: Roo.bootstrap,
26258                     items:  []
26259                 }
26260         };
26261         Roo.each(this.formats, function(f) {
26262             style.menu.items.push({
26263                 xtype :'MenuItem',
26264                 xns: Roo.bootstrap,
26265                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26266                 tagname : f,
26267                 listeners : {
26268                     click : function()
26269                     {
26270                         editorcore.insertTag(this.tagname);
26271                         editor.focus();
26272                     }
26273                 }
26274                 
26275             });
26276         });
26277         children.push(style);   
26278         
26279         btn('bold',false,true);
26280         btn('italic',false,true);
26281         btn('align-left', 'justifyleft',true);
26282         btn('align-center', 'justifycenter',true);
26283         btn('align-right' , 'justifyright',true);
26284         btn('link', false, false, function(btn) {
26285             //Roo.log("create link?");
26286             var url = prompt(this.createLinkText, this.defaultLinkValue);
26287             if(url && url != 'http:/'+'/'){
26288                 this.editorcore.relayCmd('createlink', url);
26289             }
26290         }),
26291         btn('list','insertunorderedlist',true);
26292         btn('pencil', false,true, function(btn){
26293                 Roo.log(this);
26294                 this.toggleSourceEdit(btn.pressed);
26295         });
26296         
26297         if (this.editor.btns.length > 0) {
26298             for (var i = 0; i<this.editor.btns.length; i++) {
26299                 children.push(this.editor.btns[i]);
26300             }
26301         }
26302         
26303         /*
26304         var cog = {
26305                 xtype: 'Button',
26306                 size : 'sm',
26307                 xns: Roo.bootstrap,
26308                 glyphicon : 'cog',
26309                 //html : 'submit'
26310                 menu : {
26311                     xtype: 'Menu',
26312                     xns: Roo.bootstrap,
26313                     items:  []
26314                 }
26315         };
26316         
26317         cog.menu.items.push({
26318             xtype :'MenuItem',
26319             xns: Roo.bootstrap,
26320             html : Clean styles,
26321             tagname : f,
26322             listeners : {
26323                 click : function()
26324                 {
26325                     editorcore.insertTag(this.tagname);
26326                     editor.focus();
26327                 }
26328             }
26329             
26330         });
26331        */
26332         
26333          
26334        this.xtype = 'NavSimplebar';
26335         
26336         for(var i=0;i< children.length;i++) {
26337             
26338             this.buttons.add(this.addxtypeChild(children[i]));
26339             
26340         }
26341         
26342         editor.on('editorevent', this.updateToolbar, this);
26343     },
26344     onBtnClick : function(id)
26345     {
26346        this.editorcore.relayCmd(id);
26347        this.editorcore.focus();
26348     },
26349     
26350     /**
26351      * Protected method that will not generally be called directly. It triggers
26352      * a toolbar update by reading the markup state of the current selection in the editor.
26353      */
26354     updateToolbar: function(){
26355
26356         if(!this.editorcore.activated){
26357             this.editor.onFirstFocus(); // is this neeed?
26358             return;
26359         }
26360
26361         var btns = this.buttons; 
26362         var doc = this.editorcore.doc;
26363         btns.get('bold').setActive(doc.queryCommandState('bold'));
26364         btns.get('italic').setActive(doc.queryCommandState('italic'));
26365         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26366         
26367         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26368         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26369         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26370         
26371         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26372         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26373          /*
26374         
26375         var ans = this.editorcore.getAllAncestors();
26376         if (this.formatCombo) {
26377             
26378             
26379             var store = this.formatCombo.store;
26380             this.formatCombo.setValue("");
26381             for (var i =0; i < ans.length;i++) {
26382                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26383                     // select it..
26384                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26385                     break;
26386                 }
26387             }
26388         }
26389         
26390         
26391         
26392         // hides menus... - so this cant be on a menu...
26393         Roo.bootstrap.MenuMgr.hideAll();
26394         */
26395         Roo.bootstrap.MenuMgr.hideAll();
26396         //this.editorsyncValue();
26397     },
26398     onFirstFocus: function() {
26399         this.buttons.each(function(item){
26400            item.enable();
26401         });
26402     },
26403     toggleSourceEdit : function(sourceEditMode){
26404         
26405           
26406         if(sourceEditMode){
26407             Roo.log("disabling buttons");
26408            this.buttons.each( function(item){
26409                 if(item.cmd != 'pencil'){
26410                     item.disable();
26411                 }
26412             });
26413           
26414         }else{
26415             Roo.log("enabling buttons");
26416             if(this.editorcore.initialized){
26417                 this.buttons.each( function(item){
26418                     item.enable();
26419                 });
26420             }
26421             
26422         }
26423         Roo.log("calling toggole on editor");
26424         // tell the editor that it's been pressed..
26425         this.editor.toggleSourceEdit(sourceEditMode);
26426        
26427     }
26428 });
26429
26430
26431
26432
26433  
26434 /*
26435  * - LGPL
26436  */
26437
26438 /**
26439  * @class Roo.bootstrap.Markdown
26440  * @extends Roo.bootstrap.TextArea
26441  * Bootstrap Showdown editable area
26442  * @cfg {string} content
26443  * 
26444  * @constructor
26445  * Create a new Showdown
26446  */
26447
26448 Roo.bootstrap.Markdown = function(config){
26449     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26450    
26451 };
26452
26453 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26454     
26455     editing :false,
26456     
26457     initEvents : function()
26458     {
26459         
26460         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26461         this.markdownEl = this.el.createChild({
26462             cls : 'roo-markdown-area'
26463         });
26464         this.inputEl().addClass('d-none');
26465         if (this.getValue() == '') {
26466             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26467             
26468         } else {
26469             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26470         }
26471         this.markdownEl.on('click', this.toggleTextEdit, this);
26472         this.on('blur', this.toggleTextEdit, this);
26473         this.on('specialkey', this.resizeTextArea, this);
26474     },
26475     
26476     toggleTextEdit : function()
26477     {
26478         var sh = this.markdownEl.getHeight();
26479         this.inputEl().addClass('d-none');
26480         this.markdownEl.addClass('d-none');
26481         if (!this.editing) {
26482             // show editor?
26483             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26484             this.inputEl().removeClass('d-none');
26485             this.inputEl().focus();
26486             this.editing = true;
26487             return;
26488         }
26489         // show showdown...
26490         this.updateMarkdown();
26491         this.markdownEl.removeClass('d-none');
26492         this.editing = false;
26493         return;
26494     },
26495     updateMarkdown : function()
26496     {
26497         if (this.getValue() == '') {
26498             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26499             return;
26500         }
26501  
26502         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26503     },
26504     
26505     resizeTextArea: function () {
26506         
26507         var sh = 100;
26508         Roo.log([sh, this.getValue().split("\n").length * 30]);
26509         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26510     },
26511     setValue : function(val)
26512     {
26513         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26514         if (!this.editing) {
26515             this.updateMarkdown();
26516         }
26517         
26518     },
26519     focus : function()
26520     {
26521         if (!this.editing) {
26522             this.toggleTextEdit();
26523         }
26524         
26525     }
26526
26527
26528 });
26529 /**
26530  * @class Roo.bootstrap.Table.AbstractSelectionModel
26531  * @extends Roo.util.Observable
26532  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26533  * implemented by descendant classes.  This class should not be directly instantiated.
26534  * @constructor
26535  */
26536 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26537     this.locked = false;
26538     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26539 };
26540
26541
26542 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26543     /** @ignore Called by the grid automatically. Do not call directly. */
26544     init : function(grid){
26545         this.grid = grid;
26546         this.initEvents();
26547     },
26548
26549     /**
26550      * Locks the selections.
26551      */
26552     lock : function(){
26553         this.locked = true;
26554     },
26555
26556     /**
26557      * Unlocks the selections.
26558      */
26559     unlock : function(){
26560         this.locked = false;
26561     },
26562
26563     /**
26564      * Returns true if the selections are locked.
26565      * @return {Boolean}
26566      */
26567     isLocked : function(){
26568         return this.locked;
26569     },
26570     
26571     
26572     initEvents : function ()
26573     {
26574         
26575     }
26576 });
26577 /**
26578  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26579  * @class Roo.bootstrap.Table.RowSelectionModel
26580  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26581  * It supports multiple selections and keyboard selection/navigation. 
26582  * @constructor
26583  * @param {Object} config
26584  */
26585
26586 Roo.bootstrap.Table.RowSelectionModel = function(config){
26587     Roo.apply(this, config);
26588     this.selections = new Roo.util.MixedCollection(false, function(o){
26589         return o.id;
26590     });
26591
26592     this.last = false;
26593     this.lastActive = false;
26594
26595     this.addEvents({
26596         /**
26597              * @event selectionchange
26598              * Fires when the selection changes
26599              * @param {SelectionModel} this
26600              */
26601             "selectionchange" : true,
26602         /**
26603              * @event afterselectionchange
26604              * Fires after the selection changes (eg. by key press or clicking)
26605              * @param {SelectionModel} this
26606              */
26607             "afterselectionchange" : true,
26608         /**
26609              * @event beforerowselect
26610              * Fires when a row is selected being selected, return false to cancel.
26611              * @param {SelectionModel} this
26612              * @param {Number} rowIndex The selected index
26613              * @param {Boolean} keepExisting False if other selections will be cleared
26614              */
26615             "beforerowselect" : true,
26616         /**
26617              * @event rowselect
26618              * Fires when a row is selected.
26619              * @param {SelectionModel} this
26620              * @param {Number} rowIndex The selected index
26621              * @param {Roo.data.Record} r The record
26622              */
26623             "rowselect" : true,
26624         /**
26625              * @event rowdeselect
26626              * Fires when a row is deselected.
26627              * @param {SelectionModel} this
26628              * @param {Number} rowIndex The selected index
26629              */
26630         "rowdeselect" : true
26631     });
26632     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26633     this.locked = false;
26634  };
26635
26636 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26637     /**
26638      * @cfg {Boolean} singleSelect
26639      * True to allow selection of only one row at a time (defaults to false)
26640      */
26641     singleSelect : false,
26642
26643     // private
26644     initEvents : function()
26645     {
26646
26647         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26648         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26649         //}else{ // allow click to work like normal
26650          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26651         //}
26652         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26653         this.grid.on("rowclick", this.handleMouseDown, this);
26654         
26655         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26656             "up" : function(e){
26657                 if(!e.shiftKey){
26658                     this.selectPrevious(e.shiftKey);
26659                 }else if(this.last !== false && this.lastActive !== false){
26660                     var last = this.last;
26661                     this.selectRange(this.last,  this.lastActive-1);
26662                     this.grid.getView().focusRow(this.lastActive);
26663                     if(last !== false){
26664                         this.last = last;
26665                     }
26666                 }else{
26667                     this.selectFirstRow();
26668                 }
26669                 this.fireEvent("afterselectionchange", this);
26670             },
26671             "down" : function(e){
26672                 if(!e.shiftKey){
26673                     this.selectNext(e.shiftKey);
26674                 }else if(this.last !== false && this.lastActive !== false){
26675                     var last = this.last;
26676                     this.selectRange(this.last,  this.lastActive+1);
26677                     this.grid.getView().focusRow(this.lastActive);
26678                     if(last !== false){
26679                         this.last = last;
26680                     }
26681                 }else{
26682                     this.selectFirstRow();
26683                 }
26684                 this.fireEvent("afterselectionchange", this);
26685             },
26686             scope: this
26687         });
26688         this.grid.store.on('load', function(){
26689             this.selections.clear();
26690         },this);
26691         /*
26692         var view = this.grid.view;
26693         view.on("refresh", this.onRefresh, this);
26694         view.on("rowupdated", this.onRowUpdated, this);
26695         view.on("rowremoved", this.onRemove, this);
26696         */
26697     },
26698
26699     // private
26700     onRefresh : function()
26701     {
26702         var ds = this.grid.store, i, v = this.grid.view;
26703         var s = this.selections;
26704         s.each(function(r){
26705             if((i = ds.indexOfId(r.id)) != -1){
26706                 v.onRowSelect(i);
26707             }else{
26708                 s.remove(r);
26709             }
26710         });
26711     },
26712
26713     // private
26714     onRemove : function(v, index, r){
26715         this.selections.remove(r);
26716     },
26717
26718     // private
26719     onRowUpdated : function(v, index, r){
26720         if(this.isSelected(r)){
26721             v.onRowSelect(index);
26722         }
26723     },
26724
26725     /**
26726      * Select records.
26727      * @param {Array} records The records to select
26728      * @param {Boolean} keepExisting (optional) True to keep existing selections
26729      */
26730     selectRecords : function(records, keepExisting)
26731     {
26732         if(!keepExisting){
26733             this.clearSelections();
26734         }
26735             var ds = this.grid.store;
26736         for(var i = 0, len = records.length; i < len; i++){
26737             this.selectRow(ds.indexOf(records[i]), true);
26738         }
26739     },
26740
26741     /**
26742      * Gets the number of selected rows.
26743      * @return {Number}
26744      */
26745     getCount : function(){
26746         return this.selections.length;
26747     },
26748
26749     /**
26750      * Selects the first row in the grid.
26751      */
26752     selectFirstRow : function(){
26753         this.selectRow(0);
26754     },
26755
26756     /**
26757      * Select the last row.
26758      * @param {Boolean} keepExisting (optional) True to keep existing selections
26759      */
26760     selectLastRow : function(keepExisting){
26761         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26762         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26763     },
26764
26765     /**
26766      * Selects the row immediately following the last selected row.
26767      * @param {Boolean} keepExisting (optional) True to keep existing selections
26768      */
26769     selectNext : function(keepExisting)
26770     {
26771             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26772             this.selectRow(this.last+1, keepExisting);
26773             this.grid.getView().focusRow(this.last);
26774         }
26775     },
26776
26777     /**
26778      * Selects the row that precedes the last selected row.
26779      * @param {Boolean} keepExisting (optional) True to keep existing selections
26780      */
26781     selectPrevious : function(keepExisting){
26782         if(this.last){
26783             this.selectRow(this.last-1, keepExisting);
26784             this.grid.getView().focusRow(this.last);
26785         }
26786     },
26787
26788     /**
26789      * Returns the selected records
26790      * @return {Array} Array of selected records
26791      */
26792     getSelections : function(){
26793         return [].concat(this.selections.items);
26794     },
26795
26796     /**
26797      * Returns the first selected record.
26798      * @return {Record}
26799      */
26800     getSelected : function(){
26801         return this.selections.itemAt(0);
26802     },
26803
26804
26805     /**
26806      * Clears all selections.
26807      */
26808     clearSelections : function(fast)
26809     {
26810         if(this.locked) {
26811             return;
26812         }
26813         if(fast !== true){
26814                 var ds = this.grid.store;
26815             var s = this.selections;
26816             s.each(function(r){
26817                 this.deselectRow(ds.indexOfId(r.id));
26818             }, this);
26819             s.clear();
26820         }else{
26821             this.selections.clear();
26822         }
26823         this.last = false;
26824     },
26825
26826
26827     /**
26828      * Selects all rows.
26829      */
26830     selectAll : function(){
26831         if(this.locked) {
26832             return;
26833         }
26834         this.selections.clear();
26835         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26836             this.selectRow(i, true);
26837         }
26838     },
26839
26840     /**
26841      * Returns True if there is a selection.
26842      * @return {Boolean}
26843      */
26844     hasSelection : function(){
26845         return this.selections.length > 0;
26846     },
26847
26848     /**
26849      * Returns True if the specified row is selected.
26850      * @param {Number/Record} record The record or index of the record to check
26851      * @return {Boolean}
26852      */
26853     isSelected : function(index){
26854             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26855         return (r && this.selections.key(r.id) ? true : false);
26856     },
26857
26858     /**
26859      * Returns True if the specified record id is selected.
26860      * @param {String} id The id of record to check
26861      * @return {Boolean}
26862      */
26863     isIdSelected : function(id){
26864         return (this.selections.key(id) ? true : false);
26865     },
26866
26867
26868     // private
26869     handleMouseDBClick : function(e, t){
26870         
26871     },
26872     // private
26873     handleMouseDown : function(e, t)
26874     {
26875             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26876         if(this.isLocked() || rowIndex < 0 ){
26877             return;
26878         };
26879         if(e.shiftKey && this.last !== false){
26880             var last = this.last;
26881             this.selectRange(last, rowIndex, e.ctrlKey);
26882             this.last = last; // reset the last
26883             t.focus();
26884     
26885         }else{
26886             var isSelected = this.isSelected(rowIndex);
26887             //Roo.log("select row:" + rowIndex);
26888             if(isSelected){
26889                 this.deselectRow(rowIndex);
26890             } else {
26891                         this.selectRow(rowIndex, true);
26892             }
26893     
26894             /*
26895                 if(e.button !== 0 && isSelected){
26896                 alert('rowIndex 2: ' + rowIndex);
26897                     view.focusRow(rowIndex);
26898                 }else if(e.ctrlKey && isSelected){
26899                     this.deselectRow(rowIndex);
26900                 }else if(!isSelected){
26901                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26902                     view.focusRow(rowIndex);
26903                 }
26904             */
26905         }
26906         this.fireEvent("afterselectionchange", this);
26907     },
26908     // private
26909     handleDragableRowClick :  function(grid, rowIndex, e) 
26910     {
26911         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26912             this.selectRow(rowIndex, false);
26913             grid.view.focusRow(rowIndex);
26914              this.fireEvent("afterselectionchange", this);
26915         }
26916     },
26917     
26918     /**
26919      * Selects multiple rows.
26920      * @param {Array} rows Array of the indexes of the row to select
26921      * @param {Boolean} keepExisting (optional) True to keep existing selections
26922      */
26923     selectRows : function(rows, keepExisting){
26924         if(!keepExisting){
26925             this.clearSelections();
26926         }
26927         for(var i = 0, len = rows.length; i < len; i++){
26928             this.selectRow(rows[i], true);
26929         }
26930     },
26931
26932     /**
26933      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26934      * @param {Number} startRow The index of the first row in the range
26935      * @param {Number} endRow The index of the last row in the range
26936      * @param {Boolean} keepExisting (optional) True to retain existing selections
26937      */
26938     selectRange : function(startRow, endRow, keepExisting){
26939         if(this.locked) {
26940             return;
26941         }
26942         if(!keepExisting){
26943             this.clearSelections();
26944         }
26945         if(startRow <= endRow){
26946             for(var i = startRow; i <= endRow; i++){
26947                 this.selectRow(i, true);
26948             }
26949         }else{
26950             for(var i = startRow; i >= endRow; i--){
26951                 this.selectRow(i, true);
26952             }
26953         }
26954     },
26955
26956     /**
26957      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26958      * @param {Number} startRow The index of the first row in the range
26959      * @param {Number} endRow The index of the last row in the range
26960      */
26961     deselectRange : function(startRow, endRow, preventViewNotify){
26962         if(this.locked) {
26963             return;
26964         }
26965         for(var i = startRow; i <= endRow; i++){
26966             this.deselectRow(i, preventViewNotify);
26967         }
26968     },
26969
26970     /**
26971      * Selects a row.
26972      * @param {Number} row The index of the row to select
26973      * @param {Boolean} keepExisting (optional) True to keep existing selections
26974      */
26975     selectRow : function(index, keepExisting, preventViewNotify)
26976     {
26977             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26978             return;
26979         }
26980         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26981             if(!keepExisting || this.singleSelect){
26982                 this.clearSelections();
26983             }
26984             
26985             var r = this.grid.store.getAt(index);
26986             //console.log('selectRow - record id :' + r.id);
26987             
26988             this.selections.add(r);
26989             this.last = this.lastActive = index;
26990             if(!preventViewNotify){
26991                 var proxy = new Roo.Element(
26992                                 this.grid.getRowDom(index)
26993                 );
26994                 proxy.addClass('bg-info info');
26995             }
26996             this.fireEvent("rowselect", this, index, r);
26997             this.fireEvent("selectionchange", this);
26998         }
26999     },
27000
27001     /**
27002      * Deselects a row.
27003      * @param {Number} row The index of the row to deselect
27004      */
27005     deselectRow : function(index, preventViewNotify)
27006     {
27007         if(this.locked) {
27008             return;
27009         }
27010         if(this.last == index){
27011             this.last = false;
27012         }
27013         if(this.lastActive == index){
27014             this.lastActive = false;
27015         }
27016         
27017         var r = this.grid.store.getAt(index);
27018         if (!r) {
27019             return;
27020         }
27021         
27022         this.selections.remove(r);
27023         //.console.log('deselectRow - record id :' + r.id);
27024         if(!preventViewNotify){
27025         
27026             var proxy = new Roo.Element(
27027                 this.grid.getRowDom(index)
27028             );
27029             proxy.removeClass('bg-info info');
27030         }
27031         this.fireEvent("rowdeselect", this, index);
27032         this.fireEvent("selectionchange", this);
27033     },
27034
27035     // private
27036     restoreLast : function(){
27037         if(this._last){
27038             this.last = this._last;
27039         }
27040     },
27041
27042     // private
27043     acceptsNav : function(row, col, cm){
27044         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27045     },
27046
27047     // private
27048     onEditorKey : function(field, e){
27049         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27050         if(k == e.TAB){
27051             e.stopEvent();
27052             ed.completeEdit();
27053             if(e.shiftKey){
27054                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27055             }else{
27056                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27057             }
27058         }else if(k == e.ENTER && !e.ctrlKey){
27059             e.stopEvent();
27060             ed.completeEdit();
27061             if(e.shiftKey){
27062                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27063             }else{
27064                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27065             }
27066         }else if(k == e.ESC){
27067             ed.cancelEdit();
27068         }
27069         if(newCell){
27070             g.startEditing(newCell[0], newCell[1]);
27071         }
27072     }
27073 });
27074 /*
27075  * Based on:
27076  * Ext JS Library 1.1.1
27077  * Copyright(c) 2006-2007, Ext JS, LLC.
27078  *
27079  * Originally Released Under LGPL - original licence link has changed is not relivant.
27080  *
27081  * Fork - LGPL
27082  * <script type="text/javascript">
27083  */
27084  
27085 /**
27086  * @class Roo.bootstrap.PagingToolbar
27087  * @extends Roo.bootstrap.NavSimplebar
27088  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27089  * @constructor
27090  * Create a new PagingToolbar
27091  * @param {Object} config The config object
27092  * @param {Roo.data.Store} store
27093  */
27094 Roo.bootstrap.PagingToolbar = function(config)
27095 {
27096     // old args format still supported... - xtype is prefered..
27097         // created from xtype...
27098     
27099     this.ds = config.dataSource;
27100     
27101     if (config.store && !this.ds) {
27102         this.store= Roo.factory(config.store, Roo.data);
27103         this.ds = this.store;
27104         this.ds.xmodule = this.xmodule || false;
27105     }
27106     
27107     this.toolbarItems = [];
27108     if (config.items) {
27109         this.toolbarItems = config.items;
27110     }
27111     
27112     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27113     
27114     this.cursor = 0;
27115     
27116     if (this.ds) { 
27117         this.bind(this.ds);
27118     }
27119     
27120     if (Roo.bootstrap.version == 4) {
27121         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27122     } else {
27123         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27124     }
27125     
27126 };
27127
27128 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27129     /**
27130      * @cfg {Roo.data.Store} dataSource
27131      * The underlying data store providing the paged data
27132      */
27133     /**
27134      * @cfg {String/HTMLElement/Element} container
27135      * container The id or element that will contain the toolbar
27136      */
27137     /**
27138      * @cfg {Boolean} displayInfo
27139      * True to display the displayMsg (defaults to false)
27140      */
27141     /**
27142      * @cfg {Number} pageSize
27143      * The number of records to display per page (defaults to 20)
27144      */
27145     pageSize: 20,
27146     /**
27147      * @cfg {String} displayMsg
27148      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27149      */
27150     displayMsg : 'Displaying {0} - {1} of {2}',
27151     /**
27152      * @cfg {String} emptyMsg
27153      * The message to display when no records are found (defaults to "No data to display")
27154      */
27155     emptyMsg : 'No data to display',
27156     /**
27157      * Customizable piece of the default paging text (defaults to "Page")
27158      * @type String
27159      */
27160     beforePageText : "Page",
27161     /**
27162      * Customizable piece of the default paging text (defaults to "of %0")
27163      * @type String
27164      */
27165     afterPageText : "of {0}",
27166     /**
27167      * Customizable piece of the default paging text (defaults to "First Page")
27168      * @type String
27169      */
27170     firstText : "First Page",
27171     /**
27172      * Customizable piece of the default paging text (defaults to "Previous Page")
27173      * @type String
27174      */
27175     prevText : "Previous Page",
27176     /**
27177      * Customizable piece of the default paging text (defaults to "Next Page")
27178      * @type String
27179      */
27180     nextText : "Next Page",
27181     /**
27182      * Customizable piece of the default paging text (defaults to "Last Page")
27183      * @type String
27184      */
27185     lastText : "Last Page",
27186     /**
27187      * Customizable piece of the default paging text (defaults to "Refresh")
27188      * @type String
27189      */
27190     refreshText : "Refresh",
27191
27192     buttons : false,
27193     // private
27194     onRender : function(ct, position) 
27195     {
27196         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27197         this.navgroup.parentId = this.id;
27198         this.navgroup.onRender(this.el, null);
27199         // add the buttons to the navgroup
27200         
27201         if(this.displayInfo){
27202             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27203             this.displayEl = this.el.select('.x-paging-info', true).first();
27204 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27205 //            this.displayEl = navel.el.select('span',true).first();
27206         }
27207         
27208         var _this = this;
27209         
27210         if(this.buttons){
27211             Roo.each(_this.buttons, function(e){ // this might need to use render????
27212                Roo.factory(e).render(_this.el);
27213             });
27214         }
27215             
27216         Roo.each(_this.toolbarItems, function(e) {
27217             _this.navgroup.addItem(e);
27218         });
27219         
27220         
27221         this.first = this.navgroup.addItem({
27222             tooltip: this.firstText,
27223             cls: "prev btn-outline-secondary",
27224             html : ' <i class="fa fa-step-backward"></i>',
27225             disabled: true,
27226             preventDefault: true,
27227             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27228         });
27229         
27230         this.prev =  this.navgroup.addItem({
27231             tooltip: this.prevText,
27232             cls: "prev btn-outline-secondary",
27233             html : ' <i class="fa fa-backward"></i>',
27234             disabled: true,
27235             preventDefault: true,
27236             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27237         });
27238     //this.addSeparator();
27239         
27240         
27241         var field = this.navgroup.addItem( {
27242             tagtype : 'span',
27243             cls : 'x-paging-position  btn-outline-secondary',
27244              disabled: true,
27245             html : this.beforePageText  +
27246                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27247                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27248          } ); //?? escaped?
27249         
27250         this.field = field.el.select('input', true).first();
27251         this.field.on("keydown", this.onPagingKeydown, this);
27252         this.field.on("focus", function(){this.dom.select();});
27253     
27254     
27255         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27256         //this.field.setHeight(18);
27257         //this.addSeparator();
27258         this.next = this.navgroup.addItem({
27259             tooltip: this.nextText,
27260             cls: "next btn-outline-secondary",
27261             html : ' <i class="fa fa-forward"></i>',
27262             disabled: true,
27263             preventDefault: true,
27264             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27265         });
27266         this.last = this.navgroup.addItem({
27267             tooltip: this.lastText,
27268             html : ' <i class="fa fa-step-forward"></i>',
27269             cls: "next btn-outline-secondary",
27270             disabled: true,
27271             preventDefault: true,
27272             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27273         });
27274     //this.addSeparator();
27275         this.loading = this.navgroup.addItem({
27276             tooltip: this.refreshText,
27277             cls: "btn-outline-secondary",
27278             html : ' <i class="fa fa-refresh"></i>',
27279             preventDefault: true,
27280             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27281         });
27282         
27283     },
27284
27285     // private
27286     updateInfo : function(){
27287         if(this.displayEl){
27288             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27289             var msg = count == 0 ?
27290                 this.emptyMsg :
27291                 String.format(
27292                     this.displayMsg,
27293                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27294                 );
27295             this.displayEl.update(msg);
27296         }
27297     },
27298
27299     // private
27300     onLoad : function(ds, r, o)
27301     {
27302         this.cursor = o.params.start ? o.params.start : 0;
27303         
27304         var d = this.getPageData(),
27305             ap = d.activePage,
27306             ps = d.pages;
27307         
27308         
27309         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27310         this.field.dom.value = ap;
27311         this.first.setDisabled(ap == 1);
27312         this.prev.setDisabled(ap == 1);
27313         this.next.setDisabled(ap == ps);
27314         this.last.setDisabled(ap == ps);
27315         this.loading.enable();
27316         this.updateInfo();
27317     },
27318
27319     // private
27320     getPageData : function(){
27321         var total = this.ds.getTotalCount();
27322         return {
27323             total : total,
27324             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27325             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27326         };
27327     },
27328
27329     // private
27330     onLoadError : function(){
27331         this.loading.enable();
27332     },
27333
27334     // private
27335     onPagingKeydown : function(e){
27336         var k = e.getKey();
27337         var d = this.getPageData();
27338         if(k == e.RETURN){
27339             var v = this.field.dom.value, pageNum;
27340             if(!v || isNaN(pageNum = parseInt(v, 10))){
27341                 this.field.dom.value = d.activePage;
27342                 return;
27343             }
27344             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27345             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27346             e.stopEvent();
27347         }
27348         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))
27349         {
27350           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27351           this.field.dom.value = pageNum;
27352           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27353           e.stopEvent();
27354         }
27355         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27356         {
27357           var v = this.field.dom.value, pageNum; 
27358           var increment = (e.shiftKey) ? 10 : 1;
27359           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27360                 increment *= -1;
27361           }
27362           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27363             this.field.dom.value = d.activePage;
27364             return;
27365           }
27366           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27367           {
27368             this.field.dom.value = parseInt(v, 10) + increment;
27369             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27370             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27371           }
27372           e.stopEvent();
27373         }
27374     },
27375
27376     // private
27377     beforeLoad : function(){
27378         if(this.loading){
27379             this.loading.disable();
27380         }
27381     },
27382
27383     // private
27384     onClick : function(which){
27385         
27386         var ds = this.ds;
27387         if (!ds) {
27388             return;
27389         }
27390         
27391         switch(which){
27392             case "first":
27393                 ds.load({params:{start: 0, limit: this.pageSize}});
27394             break;
27395             case "prev":
27396                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27397             break;
27398             case "next":
27399                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27400             break;
27401             case "last":
27402                 var total = ds.getTotalCount();
27403                 var extra = total % this.pageSize;
27404                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27405                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27406             break;
27407             case "refresh":
27408                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27409             break;
27410         }
27411     },
27412
27413     /**
27414      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27415      * @param {Roo.data.Store} store The data store to unbind
27416      */
27417     unbind : function(ds){
27418         ds.un("beforeload", this.beforeLoad, this);
27419         ds.un("load", this.onLoad, this);
27420         ds.un("loadexception", this.onLoadError, this);
27421         ds.un("remove", this.updateInfo, this);
27422         ds.un("add", this.updateInfo, this);
27423         this.ds = undefined;
27424     },
27425
27426     /**
27427      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27428      * @param {Roo.data.Store} store The data store to bind
27429      */
27430     bind : function(ds){
27431         ds.on("beforeload", this.beforeLoad, this);
27432         ds.on("load", this.onLoad, this);
27433         ds.on("loadexception", this.onLoadError, this);
27434         ds.on("remove", this.updateInfo, this);
27435         ds.on("add", this.updateInfo, this);
27436         this.ds = ds;
27437     }
27438 });/*
27439  * - LGPL
27440  *
27441  * element
27442  * 
27443  */
27444
27445 /**
27446  * @class Roo.bootstrap.MessageBar
27447  * @extends Roo.bootstrap.Component
27448  * Bootstrap MessageBar class
27449  * @cfg {String} html contents of the MessageBar
27450  * @cfg {String} weight (info | success | warning | danger) default info
27451  * @cfg {String} beforeClass insert the bar before the given class
27452  * @cfg {Boolean} closable (true | false) default false
27453  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27454  * 
27455  * @constructor
27456  * Create a new Element
27457  * @param {Object} config The config object
27458  */
27459
27460 Roo.bootstrap.MessageBar = function(config){
27461     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27462 };
27463
27464 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27465     
27466     html: '',
27467     weight: 'info',
27468     closable: false,
27469     fixed: false,
27470     beforeClass: 'bootstrap-sticky-wrap',
27471     
27472     getAutoCreate : function(){
27473         
27474         var cfg = {
27475             tag: 'div',
27476             cls: 'alert alert-dismissable alert-' + this.weight,
27477             cn: [
27478                 {
27479                     tag: 'span',
27480                     cls: 'message',
27481                     html: this.html || ''
27482                 }
27483             ]
27484         };
27485         
27486         if(this.fixed){
27487             cfg.cls += ' alert-messages-fixed';
27488         }
27489         
27490         if(this.closable){
27491             cfg.cn.push({
27492                 tag: 'button',
27493                 cls: 'close',
27494                 html: 'x'
27495             });
27496         }
27497         
27498         return cfg;
27499     },
27500     
27501     onRender : function(ct, position)
27502     {
27503         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27504         
27505         if(!this.el){
27506             var cfg = Roo.apply({},  this.getAutoCreate());
27507             cfg.id = Roo.id();
27508             
27509             if (this.cls) {
27510                 cfg.cls += ' ' + this.cls;
27511             }
27512             if (this.style) {
27513                 cfg.style = this.style;
27514             }
27515             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27516             
27517             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27518         }
27519         
27520         this.el.select('>button.close').on('click', this.hide, this);
27521         
27522     },
27523     
27524     show : function()
27525     {
27526         if (!this.rendered) {
27527             this.render();
27528         }
27529         
27530         this.el.show();
27531         
27532         this.fireEvent('show', this);
27533         
27534     },
27535     
27536     hide : function()
27537     {
27538         if (!this.rendered) {
27539             this.render();
27540         }
27541         
27542         this.el.hide();
27543         
27544         this.fireEvent('hide', this);
27545     },
27546     
27547     update : function()
27548     {
27549 //        var e = this.el.dom.firstChild;
27550 //        
27551 //        if(this.closable){
27552 //            e = e.nextSibling;
27553 //        }
27554 //        
27555 //        e.data = this.html || '';
27556
27557         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27558     }
27559    
27560 });
27561
27562  
27563
27564      /*
27565  * - LGPL
27566  *
27567  * Graph
27568  * 
27569  */
27570
27571
27572 /**
27573  * @class Roo.bootstrap.Graph
27574  * @extends Roo.bootstrap.Component
27575  * Bootstrap Graph class
27576 > Prameters
27577  -sm {number} sm 4
27578  -md {number} md 5
27579  @cfg {String} graphtype  bar | vbar | pie
27580  @cfg {number} g_x coodinator | centre x (pie)
27581  @cfg {number} g_y coodinator | centre y (pie)
27582  @cfg {number} g_r radius (pie)
27583  @cfg {number} g_height height of the chart (respected by all elements in the set)
27584  @cfg {number} g_width width of the chart (respected by all elements in the set)
27585  @cfg {Object} title The title of the chart
27586     
27587  -{Array}  values
27588  -opts (object) options for the chart 
27589      o {
27590      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27591      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27592      o vgutter (number)
27593      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.
27594      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27595      o to
27596      o stretch (boolean)
27597      o }
27598  -opts (object) options for the pie
27599      o{
27600      o cut
27601      o startAngle (number)
27602      o endAngle (number)
27603      } 
27604  *
27605  * @constructor
27606  * Create a new Input
27607  * @param {Object} config The config object
27608  */
27609
27610 Roo.bootstrap.Graph = function(config){
27611     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27612     
27613     this.addEvents({
27614         // img events
27615         /**
27616          * @event click
27617          * The img click event for the img.
27618          * @param {Roo.EventObject} e
27619          */
27620         "click" : true
27621     });
27622 };
27623
27624 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27625     
27626     sm: 4,
27627     md: 5,
27628     graphtype: 'bar',
27629     g_height: 250,
27630     g_width: 400,
27631     g_x: 50,
27632     g_y: 50,
27633     g_r: 30,
27634     opts:{
27635         //g_colors: this.colors,
27636         g_type: 'soft',
27637         g_gutter: '20%'
27638
27639     },
27640     title : false,
27641
27642     getAutoCreate : function(){
27643         
27644         var cfg = {
27645             tag: 'div',
27646             html : null
27647         };
27648         
27649         
27650         return  cfg;
27651     },
27652
27653     onRender : function(ct,position){
27654         
27655         
27656         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27657         
27658         if (typeof(Raphael) == 'undefined') {
27659             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27660             return;
27661         }
27662         
27663         this.raphael = Raphael(this.el.dom);
27664         
27665                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27666                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27667                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27668                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27669                 /*
27670                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27671                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27672                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27673                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27674                 
27675                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27676                 r.barchart(330, 10, 300, 220, data1);
27677                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27678                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27679                 */
27680                 
27681                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27682                 // r.barchart(30, 30, 560, 250,  xdata, {
27683                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27684                 //     axis : "0 0 1 1",
27685                 //     axisxlabels :  xdata
27686                 //     //yvalues : cols,
27687                    
27688                 // });
27689 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27690 //        
27691 //        this.load(null,xdata,{
27692 //                axis : "0 0 1 1",
27693 //                axisxlabels :  xdata
27694 //                });
27695
27696     },
27697
27698     load : function(graphtype,xdata,opts)
27699     {
27700         this.raphael.clear();
27701         if(!graphtype) {
27702             graphtype = this.graphtype;
27703         }
27704         if(!opts){
27705             opts = this.opts;
27706         }
27707         var r = this.raphael,
27708             fin = function () {
27709                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27710             },
27711             fout = function () {
27712                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27713             },
27714             pfin = function() {
27715                 this.sector.stop();
27716                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27717
27718                 if (this.label) {
27719                     this.label[0].stop();
27720                     this.label[0].attr({ r: 7.5 });
27721                     this.label[1].attr({ "font-weight": 800 });
27722                 }
27723             },
27724             pfout = function() {
27725                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27726
27727                 if (this.label) {
27728                     this.label[0].animate({ r: 5 }, 500, "bounce");
27729                     this.label[1].attr({ "font-weight": 400 });
27730                 }
27731             };
27732
27733         switch(graphtype){
27734             case 'bar':
27735                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27736                 break;
27737             case 'hbar':
27738                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27739                 break;
27740             case 'pie':
27741 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27742 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27743 //            
27744                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27745                 
27746                 break;
27747
27748         }
27749         
27750         if(this.title){
27751             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27752         }
27753         
27754     },
27755     
27756     setTitle: function(o)
27757     {
27758         this.title = o;
27759     },
27760     
27761     initEvents: function() {
27762         
27763         if(!this.href){
27764             this.el.on('click', this.onClick, this);
27765         }
27766     },
27767     
27768     onClick : function(e)
27769     {
27770         Roo.log('img onclick');
27771         this.fireEvent('click', this, e);
27772     }
27773    
27774 });
27775
27776  
27777 /*
27778  * - LGPL
27779  *
27780  * numberBox
27781  * 
27782  */
27783 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27784
27785 /**
27786  * @class Roo.bootstrap.dash.NumberBox
27787  * @extends Roo.bootstrap.Component
27788  * Bootstrap NumberBox class
27789  * @cfg {String} headline Box headline
27790  * @cfg {String} content Box content
27791  * @cfg {String} icon Box icon
27792  * @cfg {String} footer Footer text
27793  * @cfg {String} fhref Footer href
27794  * 
27795  * @constructor
27796  * Create a new NumberBox
27797  * @param {Object} config The config object
27798  */
27799
27800
27801 Roo.bootstrap.dash.NumberBox = function(config){
27802     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27803     
27804 };
27805
27806 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27807     
27808     headline : '',
27809     content : '',
27810     icon : '',
27811     footer : '',
27812     fhref : '',
27813     ficon : '',
27814     
27815     getAutoCreate : function(){
27816         
27817         var cfg = {
27818             tag : 'div',
27819             cls : 'small-box ',
27820             cn : [
27821                 {
27822                     tag : 'div',
27823                     cls : 'inner',
27824                     cn :[
27825                         {
27826                             tag : 'h3',
27827                             cls : 'roo-headline',
27828                             html : this.headline
27829                         },
27830                         {
27831                             tag : 'p',
27832                             cls : 'roo-content',
27833                             html : this.content
27834                         }
27835                     ]
27836                 }
27837             ]
27838         };
27839         
27840         if(this.icon){
27841             cfg.cn.push({
27842                 tag : 'div',
27843                 cls : 'icon',
27844                 cn :[
27845                     {
27846                         tag : 'i',
27847                         cls : 'ion ' + this.icon
27848                     }
27849                 ]
27850             });
27851         }
27852         
27853         if(this.footer){
27854             var footer = {
27855                 tag : 'a',
27856                 cls : 'small-box-footer',
27857                 href : this.fhref || '#',
27858                 html : this.footer
27859             };
27860             
27861             cfg.cn.push(footer);
27862             
27863         }
27864         
27865         return  cfg;
27866     },
27867
27868     onRender : function(ct,position){
27869         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27870
27871
27872        
27873                 
27874     },
27875
27876     setHeadline: function (value)
27877     {
27878         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27879     },
27880     
27881     setFooter: function (value, href)
27882     {
27883         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27884         
27885         if(href){
27886             this.el.select('a.small-box-footer',true).first().attr('href', href);
27887         }
27888         
27889     },
27890
27891     setContent: function (value)
27892     {
27893         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27894     },
27895
27896     initEvents: function() 
27897     {   
27898         
27899     }
27900     
27901 });
27902
27903  
27904 /*
27905  * - LGPL
27906  *
27907  * TabBox
27908  * 
27909  */
27910 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27911
27912 /**
27913  * @class Roo.bootstrap.dash.TabBox
27914  * @extends Roo.bootstrap.Component
27915  * Bootstrap TabBox class
27916  * @cfg {String} title Title of the TabBox
27917  * @cfg {String} icon Icon of the TabBox
27918  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27919  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27920  * 
27921  * @constructor
27922  * Create a new TabBox
27923  * @param {Object} config The config object
27924  */
27925
27926
27927 Roo.bootstrap.dash.TabBox = function(config){
27928     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27929     this.addEvents({
27930         // raw events
27931         /**
27932          * @event addpane
27933          * When a pane is added
27934          * @param {Roo.bootstrap.dash.TabPane} pane
27935          */
27936         "addpane" : true,
27937         /**
27938          * @event activatepane
27939          * When a pane is activated
27940          * @param {Roo.bootstrap.dash.TabPane} pane
27941          */
27942         "activatepane" : true
27943         
27944          
27945     });
27946     
27947     this.panes = [];
27948 };
27949
27950 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27951
27952     title : '',
27953     icon : false,
27954     showtabs : true,
27955     tabScrollable : false,
27956     
27957     getChildContainer : function()
27958     {
27959         return this.el.select('.tab-content', true).first();
27960     },
27961     
27962     getAutoCreate : function(){
27963         
27964         var header = {
27965             tag: 'li',
27966             cls: 'pull-left header',
27967             html: this.title,
27968             cn : []
27969         };
27970         
27971         if(this.icon){
27972             header.cn.push({
27973                 tag: 'i',
27974                 cls: 'fa ' + this.icon
27975             });
27976         }
27977         
27978         var h = {
27979             tag: 'ul',
27980             cls: 'nav nav-tabs pull-right',
27981             cn: [
27982                 header
27983             ]
27984         };
27985         
27986         if(this.tabScrollable){
27987             h = {
27988                 tag: 'div',
27989                 cls: 'tab-header',
27990                 cn: [
27991                     {
27992                         tag: 'ul',
27993                         cls: 'nav nav-tabs pull-right',
27994                         cn: [
27995                             header
27996                         ]
27997                     }
27998                 ]
27999             };
28000         }
28001         
28002         var cfg = {
28003             tag: 'div',
28004             cls: 'nav-tabs-custom',
28005             cn: [
28006                 h,
28007                 {
28008                     tag: 'div',
28009                     cls: 'tab-content no-padding',
28010                     cn: []
28011                 }
28012             ]
28013         };
28014
28015         return  cfg;
28016     },
28017     initEvents : function()
28018     {
28019         //Roo.log('add add pane handler');
28020         this.on('addpane', this.onAddPane, this);
28021     },
28022      /**
28023      * Updates the box title
28024      * @param {String} html to set the title to.
28025      */
28026     setTitle : function(value)
28027     {
28028         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28029     },
28030     onAddPane : function(pane)
28031     {
28032         this.panes.push(pane);
28033         //Roo.log('addpane');
28034         //Roo.log(pane);
28035         // tabs are rendere left to right..
28036         if(!this.showtabs){
28037             return;
28038         }
28039         
28040         var ctr = this.el.select('.nav-tabs', true).first();
28041          
28042          
28043         var existing = ctr.select('.nav-tab',true);
28044         var qty = existing.getCount();;
28045         
28046         
28047         var tab = ctr.createChild({
28048             tag : 'li',
28049             cls : 'nav-tab' + (qty ? '' : ' active'),
28050             cn : [
28051                 {
28052                     tag : 'a',
28053                     href:'#',
28054                     html : pane.title
28055                 }
28056             ]
28057         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28058         pane.tab = tab;
28059         
28060         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28061         if (!qty) {
28062             pane.el.addClass('active');
28063         }
28064         
28065                 
28066     },
28067     onTabClick : function(ev,un,ob,pane)
28068     {
28069         //Roo.log('tab - prev default');
28070         ev.preventDefault();
28071         
28072         
28073         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28074         pane.tab.addClass('active');
28075         //Roo.log(pane.title);
28076         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28077         // technically we should have a deactivate event.. but maybe add later.
28078         // and it should not de-activate the selected tab...
28079         this.fireEvent('activatepane', pane);
28080         pane.el.addClass('active');
28081         pane.fireEvent('activate');
28082         
28083         
28084     },
28085     
28086     getActivePane : function()
28087     {
28088         var r = false;
28089         Roo.each(this.panes, function(p) {
28090             if(p.el.hasClass('active')){
28091                 r = p;
28092                 return false;
28093             }
28094             
28095             return;
28096         });
28097         
28098         return r;
28099     }
28100     
28101     
28102 });
28103
28104  
28105 /*
28106  * - LGPL
28107  *
28108  * Tab pane
28109  * 
28110  */
28111 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28112 /**
28113  * @class Roo.bootstrap.TabPane
28114  * @extends Roo.bootstrap.Component
28115  * Bootstrap TabPane class
28116  * @cfg {Boolean} active (false | true) Default false
28117  * @cfg {String} title title of panel
28118
28119  * 
28120  * @constructor
28121  * Create a new TabPane
28122  * @param {Object} config The config object
28123  */
28124
28125 Roo.bootstrap.dash.TabPane = function(config){
28126     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28127     
28128     this.addEvents({
28129         // raw events
28130         /**
28131          * @event activate
28132          * When a pane is activated
28133          * @param {Roo.bootstrap.dash.TabPane} pane
28134          */
28135         "activate" : true
28136          
28137     });
28138 };
28139
28140 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28141     
28142     active : false,
28143     title : '',
28144     
28145     // the tabBox that this is attached to.
28146     tab : false,
28147      
28148     getAutoCreate : function() 
28149     {
28150         var cfg = {
28151             tag: 'div',
28152             cls: 'tab-pane'
28153         };
28154         
28155         if(this.active){
28156             cfg.cls += ' active';
28157         }
28158         
28159         return cfg;
28160     },
28161     initEvents  : function()
28162     {
28163         //Roo.log('trigger add pane handler');
28164         this.parent().fireEvent('addpane', this)
28165     },
28166     
28167      /**
28168      * Updates the tab title 
28169      * @param {String} html to set the title to.
28170      */
28171     setTitle: function(str)
28172     {
28173         if (!this.tab) {
28174             return;
28175         }
28176         this.title = str;
28177         this.tab.select('a', true).first().dom.innerHTML = str;
28178         
28179     }
28180     
28181     
28182     
28183 });
28184
28185  
28186
28187
28188  /*
28189  * - LGPL
28190  *
28191  * menu
28192  * 
28193  */
28194 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28195
28196 /**
28197  * @class Roo.bootstrap.menu.Menu
28198  * @extends Roo.bootstrap.Component
28199  * Bootstrap Menu class - container for Menu
28200  * @cfg {String} html Text of the menu
28201  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28202  * @cfg {String} icon Font awesome icon
28203  * @cfg {String} pos Menu align to (top | bottom) default bottom
28204  * 
28205  * 
28206  * @constructor
28207  * Create a new Menu
28208  * @param {Object} config The config object
28209  */
28210
28211
28212 Roo.bootstrap.menu.Menu = function(config){
28213     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28214     
28215     this.addEvents({
28216         /**
28217          * @event beforeshow
28218          * Fires before this menu is displayed
28219          * @param {Roo.bootstrap.menu.Menu} this
28220          */
28221         beforeshow : true,
28222         /**
28223          * @event beforehide
28224          * Fires before this menu is hidden
28225          * @param {Roo.bootstrap.menu.Menu} this
28226          */
28227         beforehide : true,
28228         /**
28229          * @event show
28230          * Fires after this menu is displayed
28231          * @param {Roo.bootstrap.menu.Menu} this
28232          */
28233         show : true,
28234         /**
28235          * @event hide
28236          * Fires after this menu is hidden
28237          * @param {Roo.bootstrap.menu.Menu} this
28238          */
28239         hide : true,
28240         /**
28241          * @event click
28242          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28243          * @param {Roo.bootstrap.menu.Menu} this
28244          * @param {Roo.EventObject} e
28245          */
28246         click : true
28247     });
28248     
28249 };
28250
28251 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28252     
28253     submenu : false,
28254     html : '',
28255     weight : 'default',
28256     icon : false,
28257     pos : 'bottom',
28258     
28259     
28260     getChildContainer : function() {
28261         if(this.isSubMenu){
28262             return this.el;
28263         }
28264         
28265         return this.el.select('ul.dropdown-menu', true).first();  
28266     },
28267     
28268     getAutoCreate : function()
28269     {
28270         var text = [
28271             {
28272                 tag : 'span',
28273                 cls : 'roo-menu-text',
28274                 html : this.html
28275             }
28276         ];
28277         
28278         if(this.icon){
28279             text.unshift({
28280                 tag : 'i',
28281                 cls : 'fa ' + this.icon
28282             })
28283         }
28284         
28285         
28286         var cfg = {
28287             tag : 'div',
28288             cls : 'btn-group',
28289             cn : [
28290                 {
28291                     tag : 'button',
28292                     cls : 'dropdown-button btn btn-' + this.weight,
28293                     cn : text
28294                 },
28295                 {
28296                     tag : 'button',
28297                     cls : 'dropdown-toggle btn btn-' + this.weight,
28298                     cn : [
28299                         {
28300                             tag : 'span',
28301                             cls : 'caret'
28302                         }
28303                     ]
28304                 },
28305                 {
28306                     tag : 'ul',
28307                     cls : 'dropdown-menu'
28308                 }
28309             ]
28310             
28311         };
28312         
28313         if(this.pos == 'top'){
28314             cfg.cls += ' dropup';
28315         }
28316         
28317         if(this.isSubMenu){
28318             cfg = {
28319                 tag : 'ul',
28320                 cls : 'dropdown-menu'
28321             }
28322         }
28323         
28324         return cfg;
28325     },
28326     
28327     onRender : function(ct, position)
28328     {
28329         this.isSubMenu = ct.hasClass('dropdown-submenu');
28330         
28331         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28332     },
28333     
28334     initEvents : function() 
28335     {
28336         if(this.isSubMenu){
28337             return;
28338         }
28339         
28340         this.hidden = true;
28341         
28342         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28343         this.triggerEl.on('click', this.onTriggerPress, this);
28344         
28345         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28346         this.buttonEl.on('click', this.onClick, this);
28347         
28348     },
28349     
28350     list : function()
28351     {
28352         if(this.isSubMenu){
28353             return this.el;
28354         }
28355         
28356         return this.el.select('ul.dropdown-menu', true).first();
28357     },
28358     
28359     onClick : function(e)
28360     {
28361         this.fireEvent("click", this, e);
28362     },
28363     
28364     onTriggerPress  : function(e)
28365     {   
28366         if (this.isVisible()) {
28367             this.hide();
28368         } else {
28369             this.show();
28370         }
28371     },
28372     
28373     isVisible : function(){
28374         return !this.hidden;
28375     },
28376     
28377     show : function()
28378     {
28379         this.fireEvent("beforeshow", this);
28380         
28381         this.hidden = false;
28382         this.el.addClass('open');
28383         
28384         Roo.get(document).on("mouseup", this.onMouseUp, this);
28385         
28386         this.fireEvent("show", this);
28387         
28388         
28389     },
28390     
28391     hide : function()
28392     {
28393         this.fireEvent("beforehide", this);
28394         
28395         this.hidden = true;
28396         this.el.removeClass('open');
28397         
28398         Roo.get(document).un("mouseup", this.onMouseUp);
28399         
28400         this.fireEvent("hide", this);
28401     },
28402     
28403     onMouseUp : function()
28404     {
28405         this.hide();
28406     }
28407     
28408 });
28409
28410  
28411  /*
28412  * - LGPL
28413  *
28414  * menu item
28415  * 
28416  */
28417 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28418
28419 /**
28420  * @class Roo.bootstrap.menu.Item
28421  * @extends Roo.bootstrap.Component
28422  * Bootstrap MenuItem class
28423  * @cfg {Boolean} submenu (true | false) default false
28424  * @cfg {String} html text of the item
28425  * @cfg {String} href the link
28426  * @cfg {Boolean} disable (true | false) default false
28427  * @cfg {Boolean} preventDefault (true | false) default true
28428  * @cfg {String} icon Font awesome icon
28429  * @cfg {String} pos Submenu align to (left | right) default right 
28430  * 
28431  * 
28432  * @constructor
28433  * Create a new Item
28434  * @param {Object} config The config object
28435  */
28436
28437
28438 Roo.bootstrap.menu.Item = function(config){
28439     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28440     this.addEvents({
28441         /**
28442          * @event mouseover
28443          * Fires when the mouse is hovering over this menu
28444          * @param {Roo.bootstrap.menu.Item} this
28445          * @param {Roo.EventObject} e
28446          */
28447         mouseover : true,
28448         /**
28449          * @event mouseout
28450          * Fires when the mouse exits this menu
28451          * @param {Roo.bootstrap.menu.Item} this
28452          * @param {Roo.EventObject} e
28453          */
28454         mouseout : true,
28455         // raw events
28456         /**
28457          * @event click
28458          * The raw click event for the entire grid.
28459          * @param {Roo.EventObject} e
28460          */
28461         click : true
28462     });
28463 };
28464
28465 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28466     
28467     submenu : false,
28468     href : '',
28469     html : '',
28470     preventDefault: true,
28471     disable : false,
28472     icon : false,
28473     pos : 'right',
28474     
28475     getAutoCreate : function()
28476     {
28477         var text = [
28478             {
28479                 tag : 'span',
28480                 cls : 'roo-menu-item-text',
28481                 html : this.html
28482             }
28483         ];
28484         
28485         if(this.icon){
28486             text.unshift({
28487                 tag : 'i',
28488                 cls : 'fa ' + this.icon
28489             })
28490         }
28491         
28492         var cfg = {
28493             tag : 'li',
28494             cn : [
28495                 {
28496                     tag : 'a',
28497                     href : this.href || '#',
28498                     cn : text
28499                 }
28500             ]
28501         };
28502         
28503         if(this.disable){
28504             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28505         }
28506         
28507         if(this.submenu){
28508             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28509             
28510             if(this.pos == 'left'){
28511                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28512             }
28513         }
28514         
28515         return cfg;
28516     },
28517     
28518     initEvents : function() 
28519     {
28520         this.el.on('mouseover', this.onMouseOver, this);
28521         this.el.on('mouseout', this.onMouseOut, this);
28522         
28523         this.el.select('a', true).first().on('click', this.onClick, this);
28524         
28525     },
28526     
28527     onClick : function(e)
28528     {
28529         if(this.preventDefault){
28530             e.preventDefault();
28531         }
28532         
28533         this.fireEvent("click", this, e);
28534     },
28535     
28536     onMouseOver : function(e)
28537     {
28538         if(this.submenu && this.pos == 'left'){
28539             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28540         }
28541         
28542         this.fireEvent("mouseover", this, e);
28543     },
28544     
28545     onMouseOut : function(e)
28546     {
28547         this.fireEvent("mouseout", this, e);
28548     }
28549 });
28550
28551  
28552
28553  /*
28554  * - LGPL
28555  *
28556  * menu separator
28557  * 
28558  */
28559 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28560
28561 /**
28562  * @class Roo.bootstrap.menu.Separator
28563  * @extends Roo.bootstrap.Component
28564  * Bootstrap Separator class
28565  * 
28566  * @constructor
28567  * Create a new Separator
28568  * @param {Object} config The config object
28569  */
28570
28571
28572 Roo.bootstrap.menu.Separator = function(config){
28573     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28574 };
28575
28576 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28577     
28578     getAutoCreate : function(){
28579         var cfg = {
28580             tag : 'li',
28581             cls: 'divider'
28582         };
28583         
28584         return cfg;
28585     }
28586    
28587 });
28588
28589  
28590
28591  /*
28592  * - LGPL
28593  *
28594  * Tooltip
28595  * 
28596  */
28597
28598 /**
28599  * @class Roo.bootstrap.Tooltip
28600  * Bootstrap Tooltip class
28601  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28602  * to determine which dom element triggers the tooltip.
28603  * 
28604  * It needs to add support for additional attributes like tooltip-position
28605  * 
28606  * @constructor
28607  * Create a new Toolti
28608  * @param {Object} config The config object
28609  */
28610
28611 Roo.bootstrap.Tooltip = function(config){
28612     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28613     
28614     this.alignment = Roo.bootstrap.Tooltip.alignment;
28615     
28616     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28617         this.alignment = config.alignment;
28618     }
28619     
28620 };
28621
28622 Roo.apply(Roo.bootstrap.Tooltip, {
28623     /**
28624      * @function init initialize tooltip monitoring.
28625      * @static
28626      */
28627     currentEl : false,
28628     currentTip : false,
28629     currentRegion : false,
28630     
28631     //  init : delay?
28632     
28633     init : function()
28634     {
28635         Roo.get(document).on('mouseover', this.enter ,this);
28636         Roo.get(document).on('mouseout', this.leave, this);
28637          
28638         
28639         this.currentTip = new Roo.bootstrap.Tooltip();
28640     },
28641     
28642     enter : function(ev)
28643     {
28644         var dom = ev.getTarget();
28645         
28646         //Roo.log(['enter',dom]);
28647         var el = Roo.fly(dom);
28648         if (this.currentEl) {
28649             //Roo.log(dom);
28650             //Roo.log(this.currentEl);
28651             //Roo.log(this.currentEl.contains(dom));
28652             if (this.currentEl == el) {
28653                 return;
28654             }
28655             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28656                 return;
28657             }
28658
28659         }
28660         
28661         if (this.currentTip.el) {
28662             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28663         }    
28664         //Roo.log(ev);
28665         
28666         if(!el || el.dom == document){
28667             return;
28668         }
28669         
28670         var bindEl = el;
28671         
28672         // you can not look for children, as if el is the body.. then everythign is the child..
28673         if (!el.attr('tooltip')) { //
28674             if (!el.select("[tooltip]").elements.length) {
28675                 return;
28676             }
28677             // is the mouse over this child...?
28678             bindEl = el.select("[tooltip]").first();
28679             var xy = ev.getXY();
28680             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28681                 //Roo.log("not in region.");
28682                 return;
28683             }
28684             //Roo.log("child element over..");
28685             
28686         }
28687         this.currentEl = bindEl;
28688         this.currentTip.bind(bindEl);
28689         this.currentRegion = Roo.lib.Region.getRegion(dom);
28690         this.currentTip.enter();
28691         
28692     },
28693     leave : function(ev)
28694     {
28695         var dom = ev.getTarget();
28696         //Roo.log(['leave',dom]);
28697         if (!this.currentEl) {
28698             return;
28699         }
28700         
28701         
28702         if (dom != this.currentEl.dom) {
28703             return;
28704         }
28705         var xy = ev.getXY();
28706         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28707             return;
28708         }
28709         // only activate leave if mouse cursor is outside... bounding box..
28710         
28711         
28712         
28713         
28714         if (this.currentTip) {
28715             this.currentTip.leave();
28716         }
28717         //Roo.log('clear currentEl');
28718         this.currentEl = false;
28719         
28720         
28721     },
28722     alignment : {
28723         'left' : ['r-l', [-2,0], 'right'],
28724         'right' : ['l-r', [2,0], 'left'],
28725         'bottom' : ['t-b', [0,2], 'top'],
28726         'top' : [ 'b-t', [0,-2], 'bottom']
28727     }
28728     
28729 });
28730
28731
28732 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28733     
28734     
28735     bindEl : false,
28736     
28737     delay : null, // can be { show : 300 , hide: 500}
28738     
28739     timeout : null,
28740     
28741     hoverState : null, //???
28742     
28743     placement : 'bottom', 
28744     
28745     alignment : false,
28746     
28747     getAutoCreate : function(){
28748     
28749         var cfg = {
28750            cls : 'tooltip',   
28751            role : 'tooltip',
28752            cn : [
28753                 {
28754                     cls : 'tooltip-arrow arrow'
28755                 },
28756                 {
28757                     cls : 'tooltip-inner'
28758                 }
28759            ]
28760         };
28761         
28762         return cfg;
28763     },
28764     bind : function(el)
28765     {
28766         this.bindEl = el;
28767     },
28768     
28769     initEvents : function()
28770     {
28771         this.arrowEl = this.el.select('.arrow', true).first();
28772         this.innerEl = this.el.select('.tooltip-inner', true).first();
28773     },
28774     
28775     enter : function () {
28776        
28777         if (this.timeout != null) {
28778             clearTimeout(this.timeout);
28779         }
28780         
28781         this.hoverState = 'in';
28782          //Roo.log("enter - show");
28783         if (!this.delay || !this.delay.show) {
28784             this.show();
28785             return;
28786         }
28787         var _t = this;
28788         this.timeout = setTimeout(function () {
28789             if (_t.hoverState == 'in') {
28790                 _t.show();
28791             }
28792         }, this.delay.show);
28793     },
28794     leave : function()
28795     {
28796         clearTimeout(this.timeout);
28797     
28798         this.hoverState = 'out';
28799          if (!this.delay || !this.delay.hide) {
28800             this.hide();
28801             return;
28802         }
28803        
28804         var _t = this;
28805         this.timeout = setTimeout(function () {
28806             //Roo.log("leave - timeout");
28807             
28808             if (_t.hoverState == 'out') {
28809                 _t.hide();
28810                 Roo.bootstrap.Tooltip.currentEl = false;
28811             }
28812         }, delay);
28813     },
28814     
28815     show : function (msg)
28816     {
28817         if (!this.el) {
28818             this.render(document.body);
28819         }
28820         // set content.
28821         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28822         
28823         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28824         
28825         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28826         
28827         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28828                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28829         
28830         var placement = typeof this.placement == 'function' ?
28831             this.placement.call(this, this.el, on_el) :
28832             this.placement;
28833             
28834         var autoToken = /\s?auto?\s?/i;
28835         var autoPlace = autoToken.test(placement);
28836         if (autoPlace) {
28837             placement = placement.replace(autoToken, '') || 'top';
28838         }
28839         
28840         //this.el.detach()
28841         //this.el.setXY([0,0]);
28842         this.el.show();
28843         //this.el.dom.style.display='block';
28844         
28845         //this.el.appendTo(on_el);
28846         
28847         var p = this.getPosition();
28848         var box = this.el.getBox();
28849         
28850         if (autoPlace) {
28851             // fixme..
28852         }
28853         
28854         var align = this.alignment[placement];
28855         
28856         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28857         
28858         if(placement == 'top' || placement == 'bottom'){
28859             if(xy[0] < 0){
28860                 placement = 'right';
28861             }
28862             
28863             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28864                 placement = 'left';
28865             }
28866             
28867             var scroll = Roo.select('body', true).first().getScroll();
28868             
28869             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28870                 placement = 'top';
28871             }
28872             
28873             align = this.alignment[placement];
28874             
28875             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28876             
28877         }
28878         
28879         this.el.alignTo(this.bindEl, align[0],align[1]);
28880         //var arrow = this.el.select('.arrow',true).first();
28881         //arrow.set(align[2], 
28882         
28883         this.el.addClass(placement);
28884         this.el.addClass("bs-tooltip-"+ placement);
28885         
28886         this.el.addClass('in fade show');
28887         
28888         this.hoverState = null;
28889         
28890         if (this.el.hasClass('fade')) {
28891             // fade it?
28892         }
28893         
28894         
28895         
28896         
28897         
28898     },
28899     hide : function()
28900     {
28901          
28902         if (!this.el) {
28903             return;
28904         }
28905         //this.el.setXY([0,0]);
28906         this.el.removeClass(['show', 'in']);
28907         //this.el.hide();
28908         
28909     }
28910     
28911 });
28912  
28913
28914  /*
28915  * - LGPL
28916  *
28917  * Location Picker
28918  * 
28919  */
28920
28921 /**
28922  * @class Roo.bootstrap.LocationPicker
28923  * @extends Roo.bootstrap.Component
28924  * Bootstrap LocationPicker class
28925  * @cfg {Number} latitude Position when init default 0
28926  * @cfg {Number} longitude Position when init default 0
28927  * @cfg {Number} zoom default 15
28928  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28929  * @cfg {Boolean} mapTypeControl default false
28930  * @cfg {Boolean} disableDoubleClickZoom default false
28931  * @cfg {Boolean} scrollwheel default true
28932  * @cfg {Boolean} streetViewControl default false
28933  * @cfg {Number} radius default 0
28934  * @cfg {String} locationName
28935  * @cfg {Boolean} draggable default true
28936  * @cfg {Boolean} enableAutocomplete default false
28937  * @cfg {Boolean} enableReverseGeocode default true
28938  * @cfg {String} markerTitle
28939  * 
28940  * @constructor
28941  * Create a new LocationPicker
28942  * @param {Object} config The config object
28943  */
28944
28945
28946 Roo.bootstrap.LocationPicker = function(config){
28947     
28948     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28949     
28950     this.addEvents({
28951         /**
28952          * @event initial
28953          * Fires when the picker initialized.
28954          * @param {Roo.bootstrap.LocationPicker} this
28955          * @param {Google Location} location
28956          */
28957         initial : true,
28958         /**
28959          * @event positionchanged
28960          * Fires when the picker position changed.
28961          * @param {Roo.bootstrap.LocationPicker} this
28962          * @param {Google Location} location
28963          */
28964         positionchanged : true,
28965         /**
28966          * @event resize
28967          * Fires when the map resize.
28968          * @param {Roo.bootstrap.LocationPicker} this
28969          */
28970         resize : true,
28971         /**
28972          * @event show
28973          * Fires when the map show.
28974          * @param {Roo.bootstrap.LocationPicker} this
28975          */
28976         show : true,
28977         /**
28978          * @event hide
28979          * Fires when the map hide.
28980          * @param {Roo.bootstrap.LocationPicker} this
28981          */
28982         hide : true,
28983         /**
28984          * @event mapClick
28985          * Fires when click the map.
28986          * @param {Roo.bootstrap.LocationPicker} this
28987          * @param {Map event} e
28988          */
28989         mapClick : true,
28990         /**
28991          * @event mapRightClick
28992          * Fires when right click the map.
28993          * @param {Roo.bootstrap.LocationPicker} this
28994          * @param {Map event} e
28995          */
28996         mapRightClick : true,
28997         /**
28998          * @event markerClick
28999          * Fires when click the marker.
29000          * @param {Roo.bootstrap.LocationPicker} this
29001          * @param {Map event} e
29002          */
29003         markerClick : true,
29004         /**
29005          * @event markerRightClick
29006          * Fires when right click the marker.
29007          * @param {Roo.bootstrap.LocationPicker} this
29008          * @param {Map event} e
29009          */
29010         markerRightClick : true,
29011         /**
29012          * @event OverlayViewDraw
29013          * Fires when OverlayView Draw
29014          * @param {Roo.bootstrap.LocationPicker} this
29015          */
29016         OverlayViewDraw : true,
29017         /**
29018          * @event OverlayViewOnAdd
29019          * Fires when OverlayView Draw
29020          * @param {Roo.bootstrap.LocationPicker} this
29021          */
29022         OverlayViewOnAdd : true,
29023         /**
29024          * @event OverlayViewOnRemove
29025          * Fires when OverlayView Draw
29026          * @param {Roo.bootstrap.LocationPicker} this
29027          */
29028         OverlayViewOnRemove : true,
29029         /**
29030          * @event OverlayViewShow
29031          * Fires when OverlayView Draw
29032          * @param {Roo.bootstrap.LocationPicker} this
29033          * @param {Pixel} cpx
29034          */
29035         OverlayViewShow : true,
29036         /**
29037          * @event OverlayViewHide
29038          * Fires when OverlayView Draw
29039          * @param {Roo.bootstrap.LocationPicker} this
29040          */
29041         OverlayViewHide : true,
29042         /**
29043          * @event loadexception
29044          * Fires when load google lib failed.
29045          * @param {Roo.bootstrap.LocationPicker} this
29046          */
29047         loadexception : true
29048     });
29049         
29050 };
29051
29052 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29053     
29054     gMapContext: false,
29055     
29056     latitude: 0,
29057     longitude: 0,
29058     zoom: 15,
29059     mapTypeId: false,
29060     mapTypeControl: false,
29061     disableDoubleClickZoom: false,
29062     scrollwheel: true,
29063     streetViewControl: false,
29064     radius: 0,
29065     locationName: '',
29066     draggable: true,
29067     enableAutocomplete: false,
29068     enableReverseGeocode: true,
29069     markerTitle: '',
29070     
29071     getAutoCreate: function()
29072     {
29073
29074         var cfg = {
29075             tag: 'div',
29076             cls: 'roo-location-picker'
29077         };
29078         
29079         return cfg
29080     },
29081     
29082     initEvents: function(ct, position)
29083     {       
29084         if(!this.el.getWidth() || this.isApplied()){
29085             return;
29086         }
29087         
29088         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29089         
29090         this.initial();
29091     },
29092     
29093     initial: function()
29094     {
29095         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29096             this.fireEvent('loadexception', this);
29097             return;
29098         }
29099         
29100         if(!this.mapTypeId){
29101             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29102         }
29103         
29104         this.gMapContext = this.GMapContext();
29105         
29106         this.initOverlayView();
29107         
29108         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29109         
29110         var _this = this;
29111                 
29112         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29113             _this.setPosition(_this.gMapContext.marker.position);
29114         });
29115         
29116         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29117             _this.fireEvent('mapClick', this, event);
29118             
29119         });
29120
29121         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29122             _this.fireEvent('mapRightClick', this, event);
29123             
29124         });
29125         
29126         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29127             _this.fireEvent('markerClick', this, event);
29128             
29129         });
29130
29131         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29132             _this.fireEvent('markerRightClick', this, event);
29133             
29134         });
29135         
29136         this.setPosition(this.gMapContext.location);
29137         
29138         this.fireEvent('initial', this, this.gMapContext.location);
29139     },
29140     
29141     initOverlayView: function()
29142     {
29143         var _this = this;
29144         
29145         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29146             
29147             draw: function()
29148             {
29149                 _this.fireEvent('OverlayViewDraw', _this);
29150             },
29151             
29152             onAdd: function()
29153             {
29154                 _this.fireEvent('OverlayViewOnAdd', _this);
29155             },
29156             
29157             onRemove: function()
29158             {
29159                 _this.fireEvent('OverlayViewOnRemove', _this);
29160             },
29161             
29162             show: function(cpx)
29163             {
29164                 _this.fireEvent('OverlayViewShow', _this, cpx);
29165             },
29166             
29167             hide: function()
29168             {
29169                 _this.fireEvent('OverlayViewHide', _this);
29170             }
29171             
29172         });
29173     },
29174     
29175     fromLatLngToContainerPixel: function(event)
29176     {
29177         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29178     },
29179     
29180     isApplied: function() 
29181     {
29182         return this.getGmapContext() == false ? false : true;
29183     },
29184     
29185     getGmapContext: function() 
29186     {
29187         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29188     },
29189     
29190     GMapContext: function() 
29191     {
29192         var position = new google.maps.LatLng(this.latitude, this.longitude);
29193         
29194         var _map = new google.maps.Map(this.el.dom, {
29195             center: position,
29196             zoom: this.zoom,
29197             mapTypeId: this.mapTypeId,
29198             mapTypeControl: this.mapTypeControl,
29199             disableDoubleClickZoom: this.disableDoubleClickZoom,
29200             scrollwheel: this.scrollwheel,
29201             streetViewControl: this.streetViewControl,
29202             locationName: this.locationName,
29203             draggable: this.draggable,
29204             enableAutocomplete: this.enableAutocomplete,
29205             enableReverseGeocode: this.enableReverseGeocode
29206         });
29207         
29208         var _marker = new google.maps.Marker({
29209             position: position,
29210             map: _map,
29211             title: this.markerTitle,
29212             draggable: this.draggable
29213         });
29214         
29215         return {
29216             map: _map,
29217             marker: _marker,
29218             circle: null,
29219             location: position,
29220             radius: this.radius,
29221             locationName: this.locationName,
29222             addressComponents: {
29223                 formatted_address: null,
29224                 addressLine1: null,
29225                 addressLine2: null,
29226                 streetName: null,
29227                 streetNumber: null,
29228                 city: null,
29229                 district: null,
29230                 state: null,
29231                 stateOrProvince: null
29232             },
29233             settings: this,
29234             domContainer: this.el.dom,
29235             geodecoder: new google.maps.Geocoder()
29236         };
29237     },
29238     
29239     drawCircle: function(center, radius, options) 
29240     {
29241         if (this.gMapContext.circle != null) {
29242             this.gMapContext.circle.setMap(null);
29243         }
29244         if (radius > 0) {
29245             radius *= 1;
29246             options = Roo.apply({}, options, {
29247                 strokeColor: "#0000FF",
29248                 strokeOpacity: .35,
29249                 strokeWeight: 2,
29250                 fillColor: "#0000FF",
29251                 fillOpacity: .2
29252             });
29253             
29254             options.map = this.gMapContext.map;
29255             options.radius = radius;
29256             options.center = center;
29257             this.gMapContext.circle = new google.maps.Circle(options);
29258             return this.gMapContext.circle;
29259         }
29260         
29261         return null;
29262     },
29263     
29264     setPosition: function(location) 
29265     {
29266         this.gMapContext.location = location;
29267         this.gMapContext.marker.setPosition(location);
29268         this.gMapContext.map.panTo(location);
29269         this.drawCircle(location, this.gMapContext.radius, {});
29270         
29271         var _this = this;
29272         
29273         if (this.gMapContext.settings.enableReverseGeocode) {
29274             this.gMapContext.geodecoder.geocode({
29275                 latLng: this.gMapContext.location
29276             }, function(results, status) {
29277                 
29278                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29279                     _this.gMapContext.locationName = results[0].formatted_address;
29280                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29281                     
29282                     _this.fireEvent('positionchanged', this, location);
29283                 }
29284             });
29285             
29286             return;
29287         }
29288         
29289         this.fireEvent('positionchanged', this, location);
29290     },
29291     
29292     resize: function()
29293     {
29294         google.maps.event.trigger(this.gMapContext.map, "resize");
29295         
29296         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29297         
29298         this.fireEvent('resize', this);
29299     },
29300     
29301     setPositionByLatLng: function(latitude, longitude)
29302     {
29303         this.setPosition(new google.maps.LatLng(latitude, longitude));
29304     },
29305     
29306     getCurrentPosition: function() 
29307     {
29308         return {
29309             latitude: this.gMapContext.location.lat(),
29310             longitude: this.gMapContext.location.lng()
29311         };
29312     },
29313     
29314     getAddressName: function() 
29315     {
29316         return this.gMapContext.locationName;
29317     },
29318     
29319     getAddressComponents: function() 
29320     {
29321         return this.gMapContext.addressComponents;
29322     },
29323     
29324     address_component_from_google_geocode: function(address_components) 
29325     {
29326         var result = {};
29327         
29328         for (var i = 0; i < address_components.length; i++) {
29329             var component = address_components[i];
29330             if (component.types.indexOf("postal_code") >= 0) {
29331                 result.postalCode = component.short_name;
29332             } else if (component.types.indexOf("street_number") >= 0) {
29333                 result.streetNumber = component.short_name;
29334             } else if (component.types.indexOf("route") >= 0) {
29335                 result.streetName = component.short_name;
29336             } else if (component.types.indexOf("neighborhood") >= 0) {
29337                 result.city = component.short_name;
29338             } else if (component.types.indexOf("locality") >= 0) {
29339                 result.city = component.short_name;
29340             } else if (component.types.indexOf("sublocality") >= 0) {
29341                 result.district = component.short_name;
29342             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29343                 result.stateOrProvince = component.short_name;
29344             } else if (component.types.indexOf("country") >= 0) {
29345                 result.country = component.short_name;
29346             }
29347         }
29348         
29349         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29350         result.addressLine2 = "";
29351         return result;
29352     },
29353     
29354     setZoomLevel: function(zoom)
29355     {
29356         this.gMapContext.map.setZoom(zoom);
29357     },
29358     
29359     show: function()
29360     {
29361         if(!this.el){
29362             return;
29363         }
29364         
29365         this.el.show();
29366         
29367         this.resize();
29368         
29369         this.fireEvent('show', this);
29370     },
29371     
29372     hide: function()
29373     {
29374         if(!this.el){
29375             return;
29376         }
29377         
29378         this.el.hide();
29379         
29380         this.fireEvent('hide', this);
29381     }
29382     
29383 });
29384
29385 Roo.apply(Roo.bootstrap.LocationPicker, {
29386     
29387     OverlayView : function(map, options)
29388     {
29389         options = options || {};
29390         
29391         this.setMap(map);
29392     }
29393     
29394     
29395 });/**
29396  * @class Roo.bootstrap.Alert
29397  * @extends Roo.bootstrap.Component
29398  * Bootstrap Alert class - shows an alert area box
29399  * eg
29400  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29401   Enter a valid email address
29402 </div>
29403  * @licence LGPL
29404  * @cfg {String} title The title of alert
29405  * @cfg {String} html The content of alert
29406  * @cfg {String} weight (  success | info | warning | danger )
29407  * @cfg {String} faicon font-awesomeicon
29408  * 
29409  * @constructor
29410  * Create a new alert
29411  * @param {Object} config The config object
29412  */
29413
29414
29415 Roo.bootstrap.Alert = function(config){
29416     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29417     
29418 };
29419
29420 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29421     
29422     title: '',
29423     html: '',
29424     weight: false,
29425     faicon: false,
29426     
29427     getAutoCreate : function()
29428     {
29429         
29430         var cfg = {
29431             tag : 'div',
29432             cls : 'alert',
29433             cn : [
29434                 {
29435                     tag : 'i',
29436                     cls : 'roo-alert-icon'
29437                     
29438                 },
29439                 {
29440                     tag : 'b',
29441                     cls : 'roo-alert-title',
29442                     html : this.title
29443                 },
29444                 {
29445                     tag : 'span',
29446                     cls : 'roo-alert-text',
29447                     html : this.html
29448                 }
29449             ]
29450         };
29451         
29452         if(this.faicon){
29453             cfg.cn[0].cls += ' fa ' + this.faicon;
29454         }
29455         
29456         if(this.weight){
29457             cfg.cls += ' alert-' + this.weight;
29458         }
29459         
29460         return cfg;
29461     },
29462     
29463     initEvents: function() 
29464     {
29465         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29466     },
29467     
29468     setTitle : function(str)
29469     {
29470         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29471     },
29472     
29473     setText : function(str)
29474     {
29475         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29476     },
29477     
29478     setWeight : function(weight)
29479     {
29480         if(this.weight){
29481             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29482         }
29483         
29484         this.weight = weight;
29485         
29486         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29487     },
29488     
29489     setIcon : function(icon)
29490     {
29491         if(this.faicon){
29492             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29493         }
29494         
29495         this.faicon = icon;
29496         
29497         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29498     },
29499     
29500     hide: function() 
29501     {
29502         this.el.hide();   
29503     },
29504     
29505     show: function() 
29506     {  
29507         this.el.show();   
29508     }
29509     
29510 });
29511
29512  
29513 /*
29514 * Licence: LGPL
29515 */
29516
29517 /**
29518  * @class Roo.bootstrap.UploadCropbox
29519  * @extends Roo.bootstrap.Component
29520  * Bootstrap UploadCropbox class
29521  * @cfg {String} emptyText show when image has been loaded
29522  * @cfg {String} rotateNotify show when image too small to rotate
29523  * @cfg {Number} errorTimeout default 3000
29524  * @cfg {Number} minWidth default 300
29525  * @cfg {Number} minHeight default 300
29526  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29527  * @cfg {Boolean} isDocument (true|false) default false
29528  * @cfg {String} url action url
29529  * @cfg {String} paramName default 'imageUpload'
29530  * @cfg {String} method default POST
29531  * @cfg {Boolean} loadMask (true|false) default true
29532  * @cfg {Boolean} loadingText default 'Loading...'
29533  * 
29534  * @constructor
29535  * Create a new UploadCropbox
29536  * @param {Object} config The config object
29537  */
29538
29539 Roo.bootstrap.UploadCropbox = function(config){
29540     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29541     
29542     this.addEvents({
29543         /**
29544          * @event beforeselectfile
29545          * Fire before select file
29546          * @param {Roo.bootstrap.UploadCropbox} this
29547          */
29548         "beforeselectfile" : true,
29549         /**
29550          * @event initial
29551          * Fire after initEvent
29552          * @param {Roo.bootstrap.UploadCropbox} this
29553          */
29554         "initial" : true,
29555         /**
29556          * @event crop
29557          * Fire after initEvent
29558          * @param {Roo.bootstrap.UploadCropbox} this
29559          * @param {String} data
29560          */
29561         "crop" : true,
29562         /**
29563          * @event prepare
29564          * Fire when preparing the file data
29565          * @param {Roo.bootstrap.UploadCropbox} this
29566          * @param {Object} file
29567          */
29568         "prepare" : true,
29569         /**
29570          * @event exception
29571          * Fire when get exception
29572          * @param {Roo.bootstrap.UploadCropbox} this
29573          * @param {XMLHttpRequest} xhr
29574          */
29575         "exception" : true,
29576         /**
29577          * @event beforeloadcanvas
29578          * Fire before load the canvas
29579          * @param {Roo.bootstrap.UploadCropbox} this
29580          * @param {String} src
29581          */
29582         "beforeloadcanvas" : true,
29583         /**
29584          * @event trash
29585          * Fire when trash image
29586          * @param {Roo.bootstrap.UploadCropbox} this
29587          */
29588         "trash" : true,
29589         /**
29590          * @event download
29591          * Fire when download the image
29592          * @param {Roo.bootstrap.UploadCropbox} this
29593          */
29594         "download" : true,
29595         /**
29596          * @event footerbuttonclick
29597          * Fire when footerbuttonclick
29598          * @param {Roo.bootstrap.UploadCropbox} this
29599          * @param {String} type
29600          */
29601         "footerbuttonclick" : true,
29602         /**
29603          * @event resize
29604          * Fire when resize
29605          * @param {Roo.bootstrap.UploadCropbox} this
29606          */
29607         "resize" : true,
29608         /**
29609          * @event rotate
29610          * Fire when rotate the image
29611          * @param {Roo.bootstrap.UploadCropbox} this
29612          * @param {String} pos
29613          */
29614         "rotate" : true,
29615         /**
29616          * @event inspect
29617          * Fire when inspect the file
29618          * @param {Roo.bootstrap.UploadCropbox} this
29619          * @param {Object} file
29620          */
29621         "inspect" : true,
29622         /**
29623          * @event upload
29624          * Fire when xhr upload the file
29625          * @param {Roo.bootstrap.UploadCropbox} this
29626          * @param {Object} data
29627          */
29628         "upload" : true,
29629         /**
29630          * @event arrange
29631          * Fire when arrange the file data
29632          * @param {Roo.bootstrap.UploadCropbox} this
29633          * @param {Object} formData
29634          */
29635         "arrange" : true
29636     });
29637     
29638     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29639 };
29640
29641 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29642     
29643     emptyText : 'Click to upload image',
29644     rotateNotify : 'Image is too small to rotate',
29645     errorTimeout : 3000,
29646     scale : 0,
29647     baseScale : 1,
29648     rotate : 0,
29649     dragable : false,
29650     pinching : false,
29651     mouseX : 0,
29652     mouseY : 0,
29653     cropData : false,
29654     minWidth : 300,
29655     minHeight : 300,
29656     file : false,
29657     exif : {},
29658     baseRotate : 1,
29659     cropType : 'image/jpeg',
29660     buttons : false,
29661     canvasLoaded : false,
29662     isDocument : false,
29663     method : 'POST',
29664     paramName : 'imageUpload',
29665     loadMask : true,
29666     loadingText : 'Loading...',
29667     maskEl : false,
29668     
29669     getAutoCreate : function()
29670     {
29671         var cfg = {
29672             tag : 'div',
29673             cls : 'roo-upload-cropbox',
29674             cn : [
29675                 {
29676                     tag : 'input',
29677                     cls : 'roo-upload-cropbox-selector',
29678                     type : 'file'
29679                 },
29680                 {
29681                     tag : 'div',
29682                     cls : 'roo-upload-cropbox-body',
29683                     style : 'cursor:pointer',
29684                     cn : [
29685                         {
29686                             tag : 'div',
29687                             cls : 'roo-upload-cropbox-preview'
29688                         },
29689                         {
29690                             tag : 'div',
29691                             cls : 'roo-upload-cropbox-thumb'
29692                         },
29693                         {
29694                             tag : 'div',
29695                             cls : 'roo-upload-cropbox-empty-notify',
29696                             html : this.emptyText
29697                         },
29698                         {
29699                             tag : 'div',
29700                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29701                             html : this.rotateNotify
29702                         }
29703                     ]
29704                 },
29705                 {
29706                     tag : 'div',
29707                     cls : 'roo-upload-cropbox-footer',
29708                     cn : {
29709                         tag : 'div',
29710                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29711                         cn : []
29712                     }
29713                 }
29714             ]
29715         };
29716         
29717         return cfg;
29718     },
29719     
29720     onRender : function(ct, position)
29721     {
29722         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29723         
29724         if (this.buttons.length) {
29725             
29726             Roo.each(this.buttons, function(bb) {
29727                 
29728                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29729                 
29730                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29731                 
29732             }, this);
29733         }
29734         
29735         if(this.loadMask){
29736             this.maskEl = this.el;
29737         }
29738     },
29739     
29740     initEvents : function()
29741     {
29742         this.urlAPI = (window.createObjectURL && window) || 
29743                                 (window.URL && URL.revokeObjectURL && URL) || 
29744                                 (window.webkitURL && webkitURL);
29745                         
29746         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29747         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29748         
29749         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29750         this.selectorEl.hide();
29751         
29752         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29753         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29754         
29755         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29756         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29757         this.thumbEl.hide();
29758         
29759         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29760         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29761         
29762         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29763         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29764         this.errorEl.hide();
29765         
29766         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29767         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29768         this.footerEl.hide();
29769         
29770         this.setThumbBoxSize();
29771         
29772         this.bind();
29773         
29774         this.resize();
29775         
29776         this.fireEvent('initial', this);
29777     },
29778
29779     bind : function()
29780     {
29781         var _this = this;
29782         
29783         window.addEventListener("resize", function() { _this.resize(); } );
29784         
29785         this.bodyEl.on('click', this.beforeSelectFile, this);
29786         
29787         if(Roo.isTouch){
29788             this.bodyEl.on('touchstart', this.onTouchStart, this);
29789             this.bodyEl.on('touchmove', this.onTouchMove, this);
29790             this.bodyEl.on('touchend', this.onTouchEnd, this);
29791         }
29792         
29793         if(!Roo.isTouch){
29794             this.bodyEl.on('mousedown', this.onMouseDown, this);
29795             this.bodyEl.on('mousemove', this.onMouseMove, this);
29796             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29797             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29798             Roo.get(document).on('mouseup', this.onMouseUp, this);
29799         }
29800         
29801         this.selectorEl.on('change', this.onFileSelected, this);
29802     },
29803     
29804     reset : function()
29805     {    
29806         this.scale = 0;
29807         this.baseScale = 1;
29808         this.rotate = 0;
29809         this.baseRotate = 1;
29810         this.dragable = false;
29811         this.pinching = false;
29812         this.mouseX = 0;
29813         this.mouseY = 0;
29814         this.cropData = false;
29815         this.notifyEl.dom.innerHTML = this.emptyText;
29816         
29817         this.selectorEl.dom.value = '';
29818         
29819     },
29820     
29821     resize : function()
29822     {
29823         if(this.fireEvent('resize', this) != false){
29824             this.setThumbBoxPosition();
29825             this.setCanvasPosition();
29826         }
29827     },
29828     
29829     onFooterButtonClick : function(e, el, o, type)
29830     {
29831         switch (type) {
29832             case 'rotate-left' :
29833                 this.onRotateLeft(e);
29834                 break;
29835             case 'rotate-right' :
29836                 this.onRotateRight(e);
29837                 break;
29838             case 'picture' :
29839                 this.beforeSelectFile(e);
29840                 break;
29841             case 'trash' :
29842                 this.trash(e);
29843                 break;
29844             case 'crop' :
29845                 this.crop(e);
29846                 break;
29847             case 'download' :
29848                 this.download(e);
29849                 break;
29850             default :
29851                 break;
29852         }
29853         
29854         this.fireEvent('footerbuttonclick', this, type);
29855     },
29856     
29857     beforeSelectFile : function(e)
29858     {
29859         e.preventDefault();
29860         
29861         if(this.fireEvent('beforeselectfile', this) != false){
29862             this.selectorEl.dom.click();
29863         }
29864     },
29865     
29866     onFileSelected : function(e)
29867     {
29868         e.preventDefault();
29869         
29870         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29871             return;
29872         }
29873         
29874         var file = this.selectorEl.dom.files[0];
29875         
29876         if(this.fireEvent('inspect', this, file) != false){
29877             this.prepare(file);
29878         }
29879         
29880     },
29881     
29882     trash : function(e)
29883     {
29884         this.fireEvent('trash', this);
29885     },
29886     
29887     download : function(e)
29888     {
29889         this.fireEvent('download', this);
29890     },
29891     
29892     loadCanvas : function(src)
29893     {   
29894         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29895             
29896             this.reset();
29897             
29898             this.imageEl = document.createElement('img');
29899             
29900             var _this = this;
29901             
29902             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29903             
29904             this.imageEl.src = src;
29905         }
29906     },
29907     
29908     onLoadCanvas : function()
29909     {   
29910         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29911         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29912         
29913         this.bodyEl.un('click', this.beforeSelectFile, this);
29914         
29915         this.notifyEl.hide();
29916         this.thumbEl.show();
29917         this.footerEl.show();
29918         
29919         this.baseRotateLevel();
29920         
29921         if(this.isDocument){
29922             this.setThumbBoxSize();
29923         }
29924         
29925         this.setThumbBoxPosition();
29926         
29927         this.baseScaleLevel();
29928         
29929         this.draw();
29930         
29931         this.resize();
29932         
29933         this.canvasLoaded = true;
29934         
29935         if(this.loadMask){
29936             this.maskEl.unmask();
29937         }
29938         
29939     },
29940     
29941     setCanvasPosition : function()
29942     {   
29943         if(!this.canvasEl){
29944             return;
29945         }
29946         
29947         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29948         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29949         
29950         this.previewEl.setLeft(pw);
29951         this.previewEl.setTop(ph);
29952         
29953     },
29954     
29955     onMouseDown : function(e)
29956     {   
29957         e.stopEvent();
29958         
29959         this.dragable = true;
29960         this.pinching = false;
29961         
29962         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29963             this.dragable = false;
29964             return;
29965         }
29966         
29967         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29968         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29969         
29970     },
29971     
29972     onMouseMove : function(e)
29973     {   
29974         e.stopEvent();
29975         
29976         if(!this.canvasLoaded){
29977             return;
29978         }
29979         
29980         if (!this.dragable){
29981             return;
29982         }
29983         
29984         var minX = Math.ceil(this.thumbEl.getLeft(true));
29985         var minY = Math.ceil(this.thumbEl.getTop(true));
29986         
29987         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29988         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29989         
29990         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29991         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29992         
29993         x = x - this.mouseX;
29994         y = y - this.mouseY;
29995         
29996         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29997         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29998         
29999         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30000         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30001         
30002         this.previewEl.setLeft(bgX);
30003         this.previewEl.setTop(bgY);
30004         
30005         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30006         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30007     },
30008     
30009     onMouseUp : function(e)
30010     {   
30011         e.stopEvent();
30012         
30013         this.dragable = false;
30014     },
30015     
30016     onMouseWheel : function(e)
30017     {   
30018         e.stopEvent();
30019         
30020         this.startScale = this.scale;
30021         
30022         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30023         
30024         if(!this.zoomable()){
30025             this.scale = this.startScale;
30026             return;
30027         }
30028         
30029         this.draw();
30030         
30031         return;
30032     },
30033     
30034     zoomable : function()
30035     {
30036         var minScale = this.thumbEl.getWidth() / this.minWidth;
30037         
30038         if(this.minWidth < this.minHeight){
30039             minScale = this.thumbEl.getHeight() / this.minHeight;
30040         }
30041         
30042         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30043         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30044         
30045         if(
30046                 this.isDocument &&
30047                 (this.rotate == 0 || this.rotate == 180) && 
30048                 (
30049                     width > this.imageEl.OriginWidth || 
30050                     height > this.imageEl.OriginHeight ||
30051                     (width < this.minWidth && height < this.minHeight)
30052                 )
30053         ){
30054             return false;
30055         }
30056         
30057         if(
30058                 this.isDocument &&
30059                 (this.rotate == 90 || this.rotate == 270) && 
30060                 (
30061                     width > this.imageEl.OriginWidth || 
30062                     height > this.imageEl.OriginHeight ||
30063                     (width < this.minHeight && height < this.minWidth)
30064                 )
30065         ){
30066             return false;
30067         }
30068         
30069         if(
30070                 !this.isDocument &&
30071                 (this.rotate == 0 || this.rotate == 180) && 
30072                 (
30073                     width < this.minWidth || 
30074                     width > this.imageEl.OriginWidth || 
30075                     height < this.minHeight || 
30076                     height > this.imageEl.OriginHeight
30077                 )
30078         ){
30079             return false;
30080         }
30081         
30082         if(
30083                 !this.isDocument &&
30084                 (this.rotate == 90 || this.rotate == 270) && 
30085                 (
30086                     width < this.minHeight || 
30087                     width > this.imageEl.OriginWidth || 
30088                     height < this.minWidth || 
30089                     height > this.imageEl.OriginHeight
30090                 )
30091         ){
30092             return false;
30093         }
30094         
30095         return true;
30096         
30097     },
30098     
30099     onRotateLeft : function(e)
30100     {   
30101         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30102             
30103             var minScale = this.thumbEl.getWidth() / this.minWidth;
30104             
30105             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30106             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30107             
30108             this.startScale = this.scale;
30109             
30110             while (this.getScaleLevel() < minScale){
30111             
30112                 this.scale = this.scale + 1;
30113                 
30114                 if(!this.zoomable()){
30115                     break;
30116                 }
30117                 
30118                 if(
30119                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30120                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30121                 ){
30122                     continue;
30123                 }
30124                 
30125                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30126
30127                 this.draw();
30128                 
30129                 return;
30130             }
30131             
30132             this.scale = this.startScale;
30133             
30134             this.onRotateFail();
30135             
30136             return false;
30137         }
30138         
30139         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30140
30141         if(this.isDocument){
30142             this.setThumbBoxSize();
30143             this.setThumbBoxPosition();
30144             this.setCanvasPosition();
30145         }
30146         
30147         this.draw();
30148         
30149         this.fireEvent('rotate', this, 'left');
30150         
30151     },
30152     
30153     onRotateRight : function(e)
30154     {
30155         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30156             
30157             var minScale = this.thumbEl.getWidth() / this.minWidth;
30158         
30159             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30160             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30161             
30162             this.startScale = this.scale;
30163             
30164             while (this.getScaleLevel() < minScale){
30165             
30166                 this.scale = this.scale + 1;
30167                 
30168                 if(!this.zoomable()){
30169                     break;
30170                 }
30171                 
30172                 if(
30173                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30174                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30175                 ){
30176                     continue;
30177                 }
30178                 
30179                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30180
30181                 this.draw();
30182                 
30183                 return;
30184             }
30185             
30186             this.scale = this.startScale;
30187             
30188             this.onRotateFail();
30189             
30190             return false;
30191         }
30192         
30193         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30194
30195         if(this.isDocument){
30196             this.setThumbBoxSize();
30197             this.setThumbBoxPosition();
30198             this.setCanvasPosition();
30199         }
30200         
30201         this.draw();
30202         
30203         this.fireEvent('rotate', this, 'right');
30204     },
30205     
30206     onRotateFail : function()
30207     {
30208         this.errorEl.show(true);
30209         
30210         var _this = this;
30211         
30212         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30213     },
30214     
30215     draw : function()
30216     {
30217         this.previewEl.dom.innerHTML = '';
30218         
30219         var canvasEl = document.createElement("canvas");
30220         
30221         var contextEl = canvasEl.getContext("2d");
30222         
30223         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30224         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30225         var center = this.imageEl.OriginWidth / 2;
30226         
30227         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30228             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30229             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30230             center = this.imageEl.OriginHeight / 2;
30231         }
30232         
30233         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30234         
30235         contextEl.translate(center, center);
30236         contextEl.rotate(this.rotate * Math.PI / 180);
30237
30238         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30239         
30240         this.canvasEl = document.createElement("canvas");
30241         
30242         this.contextEl = this.canvasEl.getContext("2d");
30243         
30244         switch (this.rotate) {
30245             case 0 :
30246                 
30247                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30248                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30249                 
30250                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30251                 
30252                 break;
30253             case 90 : 
30254                 
30255                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30256                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30257                 
30258                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30259                     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);
30260                     break;
30261                 }
30262                 
30263                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30264                 
30265                 break;
30266             case 180 :
30267                 
30268                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30269                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30270                 
30271                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30272                     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);
30273                     break;
30274                 }
30275                 
30276                 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);
30277                 
30278                 break;
30279             case 270 :
30280                 
30281                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30282                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30283         
30284                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30285                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30286                     break;
30287                 }
30288                 
30289                 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);
30290                 
30291                 break;
30292             default : 
30293                 break;
30294         }
30295         
30296         this.previewEl.appendChild(this.canvasEl);
30297         
30298         this.setCanvasPosition();
30299     },
30300     
30301     crop : function()
30302     {
30303         if(!this.canvasLoaded){
30304             return;
30305         }
30306         
30307         var imageCanvas = document.createElement("canvas");
30308         
30309         var imageContext = imageCanvas.getContext("2d");
30310         
30311         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30312         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30313         
30314         var center = imageCanvas.width / 2;
30315         
30316         imageContext.translate(center, center);
30317         
30318         imageContext.rotate(this.rotate * Math.PI / 180);
30319         
30320         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30321         
30322         var canvas = document.createElement("canvas");
30323         
30324         var context = canvas.getContext("2d");
30325                 
30326         canvas.width = this.minWidth;
30327         canvas.height = this.minHeight;
30328
30329         switch (this.rotate) {
30330             case 0 :
30331                 
30332                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30333                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30334                 
30335                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30336                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30337                 
30338                 var targetWidth = this.minWidth - 2 * x;
30339                 var targetHeight = this.minHeight - 2 * y;
30340                 
30341                 var scale = 1;
30342                 
30343                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30344                     scale = targetWidth / width;
30345                 }
30346                 
30347                 if(x > 0 && y == 0){
30348                     scale = targetHeight / height;
30349                 }
30350                 
30351                 if(x > 0 && y > 0){
30352                     scale = targetWidth / width;
30353                     
30354                     if(width < height){
30355                         scale = targetHeight / height;
30356                     }
30357                 }
30358                 
30359                 context.scale(scale, scale);
30360                 
30361                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30362                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30363
30364                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30365                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30366
30367                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30368                 
30369                 break;
30370             case 90 : 
30371                 
30372                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30373                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30374                 
30375                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30376                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30377                 
30378                 var targetWidth = this.minWidth - 2 * x;
30379                 var targetHeight = this.minHeight - 2 * y;
30380                 
30381                 var scale = 1;
30382                 
30383                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30384                     scale = targetWidth / width;
30385                 }
30386                 
30387                 if(x > 0 && y == 0){
30388                     scale = targetHeight / height;
30389                 }
30390                 
30391                 if(x > 0 && y > 0){
30392                     scale = targetWidth / width;
30393                     
30394                     if(width < height){
30395                         scale = targetHeight / height;
30396                     }
30397                 }
30398                 
30399                 context.scale(scale, scale);
30400                 
30401                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30402                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30403
30404                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30405                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30406                 
30407                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30408                 
30409                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30410                 
30411                 break;
30412             case 180 :
30413                 
30414                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30415                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30416                 
30417                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30418                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30419                 
30420                 var targetWidth = this.minWidth - 2 * x;
30421                 var targetHeight = this.minHeight - 2 * y;
30422                 
30423                 var scale = 1;
30424                 
30425                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30426                     scale = targetWidth / width;
30427                 }
30428                 
30429                 if(x > 0 && y == 0){
30430                     scale = targetHeight / height;
30431                 }
30432                 
30433                 if(x > 0 && y > 0){
30434                     scale = targetWidth / width;
30435                     
30436                     if(width < height){
30437                         scale = targetHeight / height;
30438                     }
30439                 }
30440                 
30441                 context.scale(scale, scale);
30442                 
30443                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30444                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30445
30446                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30447                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30448
30449                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30450                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30451                 
30452                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30453                 
30454                 break;
30455             case 270 :
30456                 
30457                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30458                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30459                 
30460                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30461                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30462                 
30463                 var targetWidth = this.minWidth - 2 * x;
30464                 var targetHeight = this.minHeight - 2 * y;
30465                 
30466                 var scale = 1;
30467                 
30468                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30469                     scale = targetWidth / width;
30470                 }
30471                 
30472                 if(x > 0 && y == 0){
30473                     scale = targetHeight / height;
30474                 }
30475                 
30476                 if(x > 0 && y > 0){
30477                     scale = targetWidth / width;
30478                     
30479                     if(width < height){
30480                         scale = targetHeight / height;
30481                     }
30482                 }
30483                 
30484                 context.scale(scale, scale);
30485                 
30486                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30487                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30488
30489                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30490                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30491                 
30492                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30493                 
30494                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30495                 
30496                 break;
30497             default : 
30498                 break;
30499         }
30500         
30501         this.cropData = canvas.toDataURL(this.cropType);
30502         
30503         if(this.fireEvent('crop', this, this.cropData) !== false){
30504             this.process(this.file, this.cropData);
30505         }
30506         
30507         return;
30508         
30509     },
30510     
30511     setThumbBoxSize : function()
30512     {
30513         var width, height;
30514         
30515         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30516             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30517             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30518             
30519             this.minWidth = width;
30520             this.minHeight = height;
30521             
30522             if(this.rotate == 90 || this.rotate == 270){
30523                 this.minWidth = height;
30524                 this.minHeight = width;
30525             }
30526         }
30527         
30528         height = 300;
30529         width = Math.ceil(this.minWidth * height / this.minHeight);
30530         
30531         if(this.minWidth > this.minHeight){
30532             width = 300;
30533             height = Math.ceil(this.minHeight * width / this.minWidth);
30534         }
30535         
30536         this.thumbEl.setStyle({
30537             width : width + 'px',
30538             height : height + 'px'
30539         });
30540
30541         return;
30542             
30543     },
30544     
30545     setThumbBoxPosition : function()
30546     {
30547         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30548         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30549         
30550         this.thumbEl.setLeft(x);
30551         this.thumbEl.setTop(y);
30552         
30553     },
30554     
30555     baseRotateLevel : function()
30556     {
30557         this.baseRotate = 1;
30558         
30559         if(
30560                 typeof(this.exif) != 'undefined' &&
30561                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30562                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30563         ){
30564             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30565         }
30566         
30567         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30568         
30569     },
30570     
30571     baseScaleLevel : function()
30572     {
30573         var width, height;
30574         
30575         if(this.isDocument){
30576             
30577             if(this.baseRotate == 6 || this.baseRotate == 8){
30578             
30579                 height = this.thumbEl.getHeight();
30580                 this.baseScale = height / this.imageEl.OriginWidth;
30581
30582                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30583                     width = this.thumbEl.getWidth();
30584                     this.baseScale = width / this.imageEl.OriginHeight;
30585                 }
30586
30587                 return;
30588             }
30589
30590             height = this.thumbEl.getHeight();
30591             this.baseScale = height / this.imageEl.OriginHeight;
30592
30593             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30594                 width = this.thumbEl.getWidth();
30595                 this.baseScale = width / this.imageEl.OriginWidth;
30596             }
30597
30598             return;
30599         }
30600         
30601         if(this.baseRotate == 6 || this.baseRotate == 8){
30602             
30603             width = this.thumbEl.getHeight();
30604             this.baseScale = width / this.imageEl.OriginHeight;
30605             
30606             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30607                 height = this.thumbEl.getWidth();
30608                 this.baseScale = height / this.imageEl.OriginHeight;
30609             }
30610             
30611             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30612                 height = this.thumbEl.getWidth();
30613                 this.baseScale = height / this.imageEl.OriginHeight;
30614                 
30615                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30616                     width = this.thumbEl.getHeight();
30617                     this.baseScale = width / this.imageEl.OriginWidth;
30618                 }
30619             }
30620             
30621             return;
30622         }
30623         
30624         width = this.thumbEl.getWidth();
30625         this.baseScale = width / this.imageEl.OriginWidth;
30626         
30627         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30628             height = this.thumbEl.getHeight();
30629             this.baseScale = height / this.imageEl.OriginHeight;
30630         }
30631         
30632         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30633             
30634             height = this.thumbEl.getHeight();
30635             this.baseScale = height / this.imageEl.OriginHeight;
30636             
30637             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30638                 width = this.thumbEl.getWidth();
30639                 this.baseScale = width / this.imageEl.OriginWidth;
30640             }
30641             
30642         }
30643         
30644         return;
30645     },
30646     
30647     getScaleLevel : function()
30648     {
30649         return this.baseScale * Math.pow(1.1, this.scale);
30650     },
30651     
30652     onTouchStart : function(e)
30653     {
30654         if(!this.canvasLoaded){
30655             this.beforeSelectFile(e);
30656             return;
30657         }
30658         
30659         var touches = e.browserEvent.touches;
30660         
30661         if(!touches){
30662             return;
30663         }
30664         
30665         if(touches.length == 1){
30666             this.onMouseDown(e);
30667             return;
30668         }
30669         
30670         if(touches.length != 2){
30671             return;
30672         }
30673         
30674         var coords = [];
30675         
30676         for(var i = 0, finger; finger = touches[i]; i++){
30677             coords.push(finger.pageX, finger.pageY);
30678         }
30679         
30680         var x = Math.pow(coords[0] - coords[2], 2);
30681         var y = Math.pow(coords[1] - coords[3], 2);
30682         
30683         this.startDistance = Math.sqrt(x + y);
30684         
30685         this.startScale = this.scale;
30686         
30687         this.pinching = true;
30688         this.dragable = false;
30689         
30690     },
30691     
30692     onTouchMove : function(e)
30693     {
30694         if(!this.pinching && !this.dragable){
30695             return;
30696         }
30697         
30698         var touches = e.browserEvent.touches;
30699         
30700         if(!touches){
30701             return;
30702         }
30703         
30704         if(this.dragable){
30705             this.onMouseMove(e);
30706             return;
30707         }
30708         
30709         var coords = [];
30710         
30711         for(var i = 0, finger; finger = touches[i]; i++){
30712             coords.push(finger.pageX, finger.pageY);
30713         }
30714         
30715         var x = Math.pow(coords[0] - coords[2], 2);
30716         var y = Math.pow(coords[1] - coords[3], 2);
30717         
30718         this.endDistance = Math.sqrt(x + y);
30719         
30720         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30721         
30722         if(!this.zoomable()){
30723             this.scale = this.startScale;
30724             return;
30725         }
30726         
30727         this.draw();
30728         
30729     },
30730     
30731     onTouchEnd : function(e)
30732     {
30733         this.pinching = false;
30734         this.dragable = false;
30735         
30736     },
30737     
30738     process : function(file, crop)
30739     {
30740         if(this.loadMask){
30741             this.maskEl.mask(this.loadingText);
30742         }
30743         
30744         this.xhr = new XMLHttpRequest();
30745         
30746         file.xhr = this.xhr;
30747
30748         this.xhr.open(this.method, this.url, true);
30749         
30750         var headers = {
30751             "Accept": "application/json",
30752             "Cache-Control": "no-cache",
30753             "X-Requested-With": "XMLHttpRequest"
30754         };
30755         
30756         for (var headerName in headers) {
30757             var headerValue = headers[headerName];
30758             if (headerValue) {
30759                 this.xhr.setRequestHeader(headerName, headerValue);
30760             }
30761         }
30762         
30763         var _this = this;
30764         
30765         this.xhr.onload = function()
30766         {
30767             _this.xhrOnLoad(_this.xhr);
30768         }
30769         
30770         this.xhr.onerror = function()
30771         {
30772             _this.xhrOnError(_this.xhr);
30773         }
30774         
30775         var formData = new FormData();
30776
30777         formData.append('returnHTML', 'NO');
30778         
30779         if(crop){
30780             formData.append('crop', crop);
30781         }
30782         
30783         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30784             formData.append(this.paramName, file, file.name);
30785         }
30786         
30787         if(typeof(file.filename) != 'undefined'){
30788             formData.append('filename', file.filename);
30789         }
30790         
30791         if(typeof(file.mimetype) != 'undefined'){
30792             formData.append('mimetype', file.mimetype);
30793         }
30794         
30795         if(this.fireEvent('arrange', this, formData) != false){
30796             this.xhr.send(formData);
30797         };
30798     },
30799     
30800     xhrOnLoad : function(xhr)
30801     {
30802         if(this.loadMask){
30803             this.maskEl.unmask();
30804         }
30805         
30806         if (xhr.readyState !== 4) {
30807             this.fireEvent('exception', this, xhr);
30808             return;
30809         }
30810
30811         var response = Roo.decode(xhr.responseText);
30812         
30813         if(!response.success){
30814             this.fireEvent('exception', this, xhr);
30815             return;
30816         }
30817         
30818         var response = Roo.decode(xhr.responseText);
30819         
30820         this.fireEvent('upload', this, response);
30821         
30822     },
30823     
30824     xhrOnError : function()
30825     {
30826         if(this.loadMask){
30827             this.maskEl.unmask();
30828         }
30829         
30830         Roo.log('xhr on error');
30831         
30832         var response = Roo.decode(xhr.responseText);
30833           
30834         Roo.log(response);
30835         
30836     },
30837     
30838     prepare : function(file)
30839     {   
30840         if(this.loadMask){
30841             this.maskEl.mask(this.loadingText);
30842         }
30843         
30844         this.file = false;
30845         this.exif = {};
30846         
30847         if(typeof(file) === 'string'){
30848             this.loadCanvas(file);
30849             return;
30850         }
30851         
30852         if(!file || !this.urlAPI){
30853             return;
30854         }
30855         
30856         this.file = file;
30857         this.cropType = file.type;
30858         
30859         var _this = this;
30860         
30861         if(this.fireEvent('prepare', this, this.file) != false){
30862             
30863             var reader = new FileReader();
30864             
30865             reader.onload = function (e) {
30866                 if (e.target.error) {
30867                     Roo.log(e.target.error);
30868                     return;
30869                 }
30870                 
30871                 var buffer = e.target.result,
30872                     dataView = new DataView(buffer),
30873                     offset = 2,
30874                     maxOffset = dataView.byteLength - 4,
30875                     markerBytes,
30876                     markerLength;
30877                 
30878                 if (dataView.getUint16(0) === 0xffd8) {
30879                     while (offset < maxOffset) {
30880                         markerBytes = dataView.getUint16(offset);
30881                         
30882                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30883                             markerLength = dataView.getUint16(offset + 2) + 2;
30884                             if (offset + markerLength > dataView.byteLength) {
30885                                 Roo.log('Invalid meta data: Invalid segment size.');
30886                                 break;
30887                             }
30888                             
30889                             if(markerBytes == 0xffe1){
30890                                 _this.parseExifData(
30891                                     dataView,
30892                                     offset,
30893                                     markerLength
30894                                 );
30895                             }
30896                             
30897                             offset += markerLength;
30898                             
30899                             continue;
30900                         }
30901                         
30902                         break;
30903                     }
30904                     
30905                 }
30906                 
30907                 var url = _this.urlAPI.createObjectURL(_this.file);
30908                 
30909                 _this.loadCanvas(url);
30910                 
30911                 return;
30912             }
30913             
30914             reader.readAsArrayBuffer(this.file);
30915             
30916         }
30917         
30918     },
30919     
30920     parseExifData : function(dataView, offset, length)
30921     {
30922         var tiffOffset = offset + 10,
30923             littleEndian,
30924             dirOffset;
30925     
30926         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30927             // No Exif data, might be XMP data instead
30928             return;
30929         }
30930         
30931         // Check for the ASCII code for "Exif" (0x45786966):
30932         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30933             // No Exif data, might be XMP data instead
30934             return;
30935         }
30936         if (tiffOffset + 8 > dataView.byteLength) {
30937             Roo.log('Invalid Exif data: Invalid segment size.');
30938             return;
30939         }
30940         // Check for the two null bytes:
30941         if (dataView.getUint16(offset + 8) !== 0x0000) {
30942             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30943             return;
30944         }
30945         // Check the byte alignment:
30946         switch (dataView.getUint16(tiffOffset)) {
30947         case 0x4949:
30948             littleEndian = true;
30949             break;
30950         case 0x4D4D:
30951             littleEndian = false;
30952             break;
30953         default:
30954             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30955             return;
30956         }
30957         // Check for the TIFF tag marker (0x002A):
30958         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30959             Roo.log('Invalid Exif data: Missing TIFF marker.');
30960             return;
30961         }
30962         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30963         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30964         
30965         this.parseExifTags(
30966             dataView,
30967             tiffOffset,
30968             tiffOffset + dirOffset,
30969             littleEndian
30970         );
30971     },
30972     
30973     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30974     {
30975         var tagsNumber,
30976             dirEndOffset,
30977             i;
30978         if (dirOffset + 6 > dataView.byteLength) {
30979             Roo.log('Invalid Exif data: Invalid directory offset.');
30980             return;
30981         }
30982         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30983         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30984         if (dirEndOffset + 4 > dataView.byteLength) {
30985             Roo.log('Invalid Exif data: Invalid directory size.');
30986             return;
30987         }
30988         for (i = 0; i < tagsNumber; i += 1) {
30989             this.parseExifTag(
30990                 dataView,
30991                 tiffOffset,
30992                 dirOffset + 2 + 12 * i, // tag offset
30993                 littleEndian
30994             );
30995         }
30996         // Return the offset to the next directory:
30997         return dataView.getUint32(dirEndOffset, littleEndian);
30998     },
30999     
31000     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31001     {
31002         var tag = dataView.getUint16(offset, littleEndian);
31003         
31004         this.exif[tag] = this.getExifValue(
31005             dataView,
31006             tiffOffset,
31007             offset,
31008             dataView.getUint16(offset + 2, littleEndian), // tag type
31009             dataView.getUint32(offset + 4, littleEndian), // tag length
31010             littleEndian
31011         );
31012     },
31013     
31014     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31015     {
31016         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31017             tagSize,
31018             dataOffset,
31019             values,
31020             i,
31021             str,
31022             c;
31023     
31024         if (!tagType) {
31025             Roo.log('Invalid Exif data: Invalid tag type.');
31026             return;
31027         }
31028         
31029         tagSize = tagType.size * length;
31030         // Determine if the value is contained in the dataOffset bytes,
31031         // or if the value at the dataOffset is a pointer to the actual data:
31032         dataOffset = tagSize > 4 ?
31033                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31034         if (dataOffset + tagSize > dataView.byteLength) {
31035             Roo.log('Invalid Exif data: Invalid data offset.');
31036             return;
31037         }
31038         if (length === 1) {
31039             return tagType.getValue(dataView, dataOffset, littleEndian);
31040         }
31041         values = [];
31042         for (i = 0; i < length; i += 1) {
31043             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31044         }
31045         
31046         if (tagType.ascii) {
31047             str = '';
31048             // Concatenate the chars:
31049             for (i = 0; i < values.length; i += 1) {
31050                 c = values[i];
31051                 // Ignore the terminating NULL byte(s):
31052                 if (c === '\u0000') {
31053                     break;
31054                 }
31055                 str += c;
31056             }
31057             return str;
31058         }
31059         return values;
31060     }
31061     
31062 });
31063
31064 Roo.apply(Roo.bootstrap.UploadCropbox, {
31065     tags : {
31066         'Orientation': 0x0112
31067     },
31068     
31069     Orientation: {
31070             1: 0, //'top-left',
31071 //            2: 'top-right',
31072             3: 180, //'bottom-right',
31073 //            4: 'bottom-left',
31074 //            5: 'left-top',
31075             6: 90, //'right-top',
31076 //            7: 'right-bottom',
31077             8: 270 //'left-bottom'
31078     },
31079     
31080     exifTagTypes : {
31081         // byte, 8-bit unsigned int:
31082         1: {
31083             getValue: function (dataView, dataOffset) {
31084                 return dataView.getUint8(dataOffset);
31085             },
31086             size: 1
31087         },
31088         // ascii, 8-bit byte:
31089         2: {
31090             getValue: function (dataView, dataOffset) {
31091                 return String.fromCharCode(dataView.getUint8(dataOffset));
31092             },
31093             size: 1,
31094             ascii: true
31095         },
31096         // short, 16 bit int:
31097         3: {
31098             getValue: function (dataView, dataOffset, littleEndian) {
31099                 return dataView.getUint16(dataOffset, littleEndian);
31100             },
31101             size: 2
31102         },
31103         // long, 32 bit int:
31104         4: {
31105             getValue: function (dataView, dataOffset, littleEndian) {
31106                 return dataView.getUint32(dataOffset, littleEndian);
31107             },
31108             size: 4
31109         },
31110         // rational = two long values, first is numerator, second is denominator:
31111         5: {
31112             getValue: function (dataView, dataOffset, littleEndian) {
31113                 return dataView.getUint32(dataOffset, littleEndian) /
31114                     dataView.getUint32(dataOffset + 4, littleEndian);
31115             },
31116             size: 8
31117         },
31118         // slong, 32 bit signed int:
31119         9: {
31120             getValue: function (dataView, dataOffset, littleEndian) {
31121                 return dataView.getInt32(dataOffset, littleEndian);
31122             },
31123             size: 4
31124         },
31125         // srational, two slongs, first is numerator, second is denominator:
31126         10: {
31127             getValue: function (dataView, dataOffset, littleEndian) {
31128                 return dataView.getInt32(dataOffset, littleEndian) /
31129                     dataView.getInt32(dataOffset + 4, littleEndian);
31130             },
31131             size: 8
31132         }
31133     },
31134     
31135     footer : {
31136         STANDARD : [
31137             {
31138                 tag : 'div',
31139                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31140                 action : 'rotate-left',
31141                 cn : [
31142                     {
31143                         tag : 'button',
31144                         cls : 'btn btn-default',
31145                         html : '<i class="fa fa-undo"></i>'
31146                     }
31147                 ]
31148             },
31149             {
31150                 tag : 'div',
31151                 cls : 'btn-group roo-upload-cropbox-picture',
31152                 action : 'picture',
31153                 cn : [
31154                     {
31155                         tag : 'button',
31156                         cls : 'btn btn-default',
31157                         html : '<i class="fa fa-picture-o"></i>'
31158                     }
31159                 ]
31160             },
31161             {
31162                 tag : 'div',
31163                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31164                 action : 'rotate-right',
31165                 cn : [
31166                     {
31167                         tag : 'button',
31168                         cls : 'btn btn-default',
31169                         html : '<i class="fa fa-repeat"></i>'
31170                     }
31171                 ]
31172             }
31173         ],
31174         DOCUMENT : [
31175             {
31176                 tag : 'div',
31177                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31178                 action : 'rotate-left',
31179                 cn : [
31180                     {
31181                         tag : 'button',
31182                         cls : 'btn btn-default',
31183                         html : '<i class="fa fa-undo"></i>'
31184                     }
31185                 ]
31186             },
31187             {
31188                 tag : 'div',
31189                 cls : 'btn-group roo-upload-cropbox-download',
31190                 action : 'download',
31191                 cn : [
31192                     {
31193                         tag : 'button',
31194                         cls : 'btn btn-default',
31195                         html : '<i class="fa fa-download"></i>'
31196                     }
31197                 ]
31198             },
31199             {
31200                 tag : 'div',
31201                 cls : 'btn-group roo-upload-cropbox-crop',
31202                 action : 'crop',
31203                 cn : [
31204                     {
31205                         tag : 'button',
31206                         cls : 'btn btn-default',
31207                         html : '<i class="fa fa-crop"></i>'
31208                     }
31209                 ]
31210             },
31211             {
31212                 tag : 'div',
31213                 cls : 'btn-group roo-upload-cropbox-trash',
31214                 action : 'trash',
31215                 cn : [
31216                     {
31217                         tag : 'button',
31218                         cls : 'btn btn-default',
31219                         html : '<i class="fa fa-trash"></i>'
31220                     }
31221                 ]
31222             },
31223             {
31224                 tag : 'div',
31225                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31226                 action : 'rotate-right',
31227                 cn : [
31228                     {
31229                         tag : 'button',
31230                         cls : 'btn btn-default',
31231                         html : '<i class="fa fa-repeat"></i>'
31232                     }
31233                 ]
31234             }
31235         ],
31236         ROTATOR : [
31237             {
31238                 tag : 'div',
31239                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31240                 action : 'rotate-left',
31241                 cn : [
31242                     {
31243                         tag : 'button',
31244                         cls : 'btn btn-default',
31245                         html : '<i class="fa fa-undo"></i>'
31246                     }
31247                 ]
31248             },
31249             {
31250                 tag : 'div',
31251                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31252                 action : 'rotate-right',
31253                 cn : [
31254                     {
31255                         tag : 'button',
31256                         cls : 'btn btn-default',
31257                         html : '<i class="fa fa-repeat"></i>'
31258                     }
31259                 ]
31260             }
31261         ]
31262     }
31263 });
31264
31265 /*
31266 * Licence: LGPL
31267 */
31268
31269 /**
31270  * @class Roo.bootstrap.DocumentManager
31271  * @extends Roo.bootstrap.Component
31272  * Bootstrap DocumentManager class
31273  * @cfg {String} paramName default 'imageUpload'
31274  * @cfg {String} toolTipName default 'filename'
31275  * @cfg {String} method default POST
31276  * @cfg {String} url action url
31277  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31278  * @cfg {Boolean} multiple multiple upload default true
31279  * @cfg {Number} thumbSize default 300
31280  * @cfg {String} fieldLabel
31281  * @cfg {Number} labelWidth default 4
31282  * @cfg {String} labelAlign (left|top) default left
31283  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31284 * @cfg {Number} labellg set the width of label (1-12)
31285  * @cfg {Number} labelmd set the width of label (1-12)
31286  * @cfg {Number} labelsm set the width of label (1-12)
31287  * @cfg {Number} labelxs set the width of label (1-12)
31288  * 
31289  * @constructor
31290  * Create a new DocumentManager
31291  * @param {Object} config The config object
31292  */
31293
31294 Roo.bootstrap.DocumentManager = function(config){
31295     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31296     
31297     this.files = [];
31298     this.delegates = [];
31299     
31300     this.addEvents({
31301         /**
31302          * @event initial
31303          * Fire when initial the DocumentManager
31304          * @param {Roo.bootstrap.DocumentManager} this
31305          */
31306         "initial" : true,
31307         /**
31308          * @event inspect
31309          * inspect selected file
31310          * @param {Roo.bootstrap.DocumentManager} this
31311          * @param {File} file
31312          */
31313         "inspect" : true,
31314         /**
31315          * @event exception
31316          * Fire when xhr load exception
31317          * @param {Roo.bootstrap.DocumentManager} this
31318          * @param {XMLHttpRequest} xhr
31319          */
31320         "exception" : true,
31321         /**
31322          * @event afterupload
31323          * Fire when xhr load exception
31324          * @param {Roo.bootstrap.DocumentManager} this
31325          * @param {XMLHttpRequest} xhr
31326          */
31327         "afterupload" : true,
31328         /**
31329          * @event prepare
31330          * prepare the form data
31331          * @param {Roo.bootstrap.DocumentManager} this
31332          * @param {Object} formData
31333          */
31334         "prepare" : true,
31335         /**
31336          * @event remove
31337          * Fire when remove the file
31338          * @param {Roo.bootstrap.DocumentManager} this
31339          * @param {Object} file
31340          */
31341         "remove" : true,
31342         /**
31343          * @event refresh
31344          * Fire after refresh the file
31345          * @param {Roo.bootstrap.DocumentManager} this
31346          */
31347         "refresh" : true,
31348         /**
31349          * @event click
31350          * Fire after click the image
31351          * @param {Roo.bootstrap.DocumentManager} this
31352          * @param {Object} file
31353          */
31354         "click" : true,
31355         /**
31356          * @event edit
31357          * Fire when upload a image and editable set to true
31358          * @param {Roo.bootstrap.DocumentManager} this
31359          * @param {Object} file
31360          */
31361         "edit" : true,
31362         /**
31363          * @event beforeselectfile
31364          * Fire before select file
31365          * @param {Roo.bootstrap.DocumentManager} this
31366          */
31367         "beforeselectfile" : true,
31368         /**
31369          * @event process
31370          * Fire before process file
31371          * @param {Roo.bootstrap.DocumentManager} this
31372          * @param {Object} file
31373          */
31374         "process" : true,
31375         /**
31376          * @event previewrendered
31377          * Fire when preview rendered
31378          * @param {Roo.bootstrap.DocumentManager} this
31379          * @param {Object} file
31380          */
31381         "previewrendered" : true,
31382         /**
31383          */
31384         "previewResize" : true
31385         
31386     });
31387 };
31388
31389 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31390     
31391     boxes : 0,
31392     inputName : '',
31393     thumbSize : 300,
31394     multiple : true,
31395     files : false,
31396     method : 'POST',
31397     url : '',
31398     paramName : 'imageUpload',
31399     toolTipName : 'filename',
31400     fieldLabel : '',
31401     labelWidth : 4,
31402     labelAlign : 'left',
31403     editable : true,
31404     delegates : false,
31405     xhr : false, 
31406     
31407     labellg : 0,
31408     labelmd : 0,
31409     labelsm : 0,
31410     labelxs : 0,
31411     
31412     getAutoCreate : function()
31413     {   
31414         var managerWidget = {
31415             tag : 'div',
31416             cls : 'roo-document-manager',
31417             cn : [
31418                 {
31419                     tag : 'input',
31420                     cls : 'roo-document-manager-selector',
31421                     type : 'file'
31422                 },
31423                 {
31424                     tag : 'div',
31425                     cls : 'roo-document-manager-uploader',
31426                     cn : [
31427                         {
31428                             tag : 'div',
31429                             cls : 'roo-document-manager-upload-btn',
31430                             html : '<i class="fa fa-plus"></i>'
31431                         }
31432                     ]
31433                     
31434                 }
31435             ]
31436         };
31437         
31438         var content = [
31439             {
31440                 tag : 'div',
31441                 cls : 'column col-md-12',
31442                 cn : managerWidget
31443             }
31444         ];
31445         
31446         if(this.fieldLabel.length){
31447             
31448             content = [
31449                 {
31450                     tag : 'div',
31451                     cls : 'column col-md-12',
31452                     html : this.fieldLabel
31453                 },
31454                 {
31455                     tag : 'div',
31456                     cls : 'column col-md-12',
31457                     cn : managerWidget
31458                 }
31459             ];
31460
31461             if(this.labelAlign == 'left'){
31462                 content = [
31463                     {
31464                         tag : 'div',
31465                         cls : 'column',
31466                         html : this.fieldLabel
31467                     },
31468                     {
31469                         tag : 'div',
31470                         cls : 'column',
31471                         cn : managerWidget
31472                     }
31473                 ];
31474                 
31475                 if(this.labelWidth > 12){
31476                     content[0].style = "width: " + this.labelWidth + 'px';
31477                 }
31478
31479                 if(this.labelWidth < 13 && this.labelmd == 0){
31480                     this.labelmd = this.labelWidth;
31481                 }
31482
31483                 if(this.labellg > 0){
31484                     content[0].cls += ' col-lg-' + this.labellg;
31485                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31486                 }
31487
31488                 if(this.labelmd > 0){
31489                     content[0].cls += ' col-md-' + this.labelmd;
31490                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31491                 }
31492
31493                 if(this.labelsm > 0){
31494                     content[0].cls += ' col-sm-' + this.labelsm;
31495                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31496                 }
31497
31498                 if(this.labelxs > 0){
31499                     content[0].cls += ' col-xs-' + this.labelxs;
31500                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31501                 }
31502                 
31503             }
31504         }
31505         
31506         var cfg = {
31507             tag : 'div',
31508             cls : 'row clearfix',
31509             cn : content
31510         };
31511         
31512         return cfg;
31513         
31514     },
31515     
31516     initEvents : function()
31517     {
31518         this.managerEl = this.el.select('.roo-document-manager', true).first();
31519         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31520         
31521         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31522         this.selectorEl.hide();
31523         
31524         if(this.multiple){
31525             this.selectorEl.attr('multiple', 'multiple');
31526         }
31527         
31528         this.selectorEl.on('change', this.onFileSelected, this);
31529         
31530         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31531         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31532         
31533         this.uploader.on('click', this.onUploaderClick, this);
31534         
31535         this.renderProgressDialog();
31536         
31537         var _this = this;
31538         
31539         window.addEventListener("resize", function() { _this.refresh(); } );
31540         
31541         this.fireEvent('initial', this);
31542     },
31543     
31544     renderProgressDialog : function()
31545     {
31546         var _this = this;
31547         
31548         this.progressDialog = new Roo.bootstrap.Modal({
31549             cls : 'roo-document-manager-progress-dialog',
31550             allow_close : false,
31551             animate : false,
31552             title : '',
31553             buttons : [
31554                 {
31555                     name  :'cancel',
31556                     weight : 'danger',
31557                     html : 'Cancel'
31558                 }
31559             ], 
31560             listeners : { 
31561                 btnclick : function() {
31562                     _this.uploadCancel();
31563                     this.hide();
31564                 }
31565             }
31566         });
31567          
31568         this.progressDialog.render(Roo.get(document.body));
31569          
31570         this.progress = new Roo.bootstrap.Progress({
31571             cls : 'roo-document-manager-progress',
31572             active : true,
31573             striped : true
31574         });
31575         
31576         this.progress.render(this.progressDialog.getChildContainer());
31577         
31578         this.progressBar = new Roo.bootstrap.ProgressBar({
31579             cls : 'roo-document-manager-progress-bar',
31580             aria_valuenow : 0,
31581             aria_valuemin : 0,
31582             aria_valuemax : 12,
31583             panel : 'success'
31584         });
31585         
31586         this.progressBar.render(this.progress.getChildContainer());
31587     },
31588     
31589     onUploaderClick : function(e)
31590     {
31591         e.preventDefault();
31592      
31593         if(this.fireEvent('beforeselectfile', this) != false){
31594             this.selectorEl.dom.click();
31595         }
31596         
31597     },
31598     
31599     onFileSelected : function(e)
31600     {
31601         e.preventDefault();
31602         
31603         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31604             return;
31605         }
31606         
31607         Roo.each(this.selectorEl.dom.files, function(file){
31608             if(this.fireEvent('inspect', this, file) != false){
31609                 this.files.push(file);
31610             }
31611         }, this);
31612         
31613         this.queue();
31614         
31615     },
31616     
31617     queue : function()
31618     {
31619         this.selectorEl.dom.value = '';
31620         
31621         if(!this.files || !this.files.length){
31622             return;
31623         }
31624         
31625         if(this.boxes > 0 && this.files.length > this.boxes){
31626             this.files = this.files.slice(0, this.boxes);
31627         }
31628         
31629         this.uploader.show();
31630         
31631         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31632             this.uploader.hide();
31633         }
31634         
31635         var _this = this;
31636         
31637         var files = [];
31638         
31639         var docs = [];
31640         
31641         Roo.each(this.files, function(file){
31642             
31643             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31644                 var f = this.renderPreview(file);
31645                 files.push(f);
31646                 return;
31647             }
31648             
31649             if(file.type.indexOf('image') != -1){
31650                 this.delegates.push(
31651                     (function(){
31652                         _this.process(file);
31653                     }).createDelegate(this)
31654                 );
31655         
31656                 return;
31657             }
31658             
31659             docs.push(
31660                 (function(){
31661                     _this.process(file);
31662                 }).createDelegate(this)
31663             );
31664             
31665         }, this);
31666         
31667         this.files = files;
31668         
31669         this.delegates = this.delegates.concat(docs);
31670         
31671         if(!this.delegates.length){
31672             this.refresh();
31673             return;
31674         }
31675         
31676         this.progressBar.aria_valuemax = this.delegates.length;
31677         
31678         this.arrange();
31679         
31680         return;
31681     },
31682     
31683     arrange : function()
31684     {
31685         if(!this.delegates.length){
31686             this.progressDialog.hide();
31687             this.refresh();
31688             return;
31689         }
31690         
31691         var delegate = this.delegates.shift();
31692         
31693         this.progressDialog.show();
31694         
31695         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31696         
31697         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31698         
31699         delegate();
31700     },
31701     
31702     refresh : function()
31703     {
31704         this.uploader.show();
31705         
31706         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31707             this.uploader.hide();
31708         }
31709         
31710         Roo.isTouch ? this.closable(false) : this.closable(true);
31711         
31712         this.fireEvent('refresh', this);
31713     },
31714     
31715     onRemove : function(e, el, o)
31716     {
31717         e.preventDefault();
31718         
31719         this.fireEvent('remove', this, o);
31720         
31721     },
31722     
31723     remove : function(o)
31724     {
31725         var files = [];
31726         
31727         Roo.each(this.files, function(file){
31728             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31729                 files.push(file);
31730                 return;
31731             }
31732
31733             o.target.remove();
31734
31735         }, this);
31736         
31737         this.files = files;
31738         
31739         this.refresh();
31740     },
31741     
31742     clear : function()
31743     {
31744         Roo.each(this.files, function(file){
31745             if(!file.target){
31746                 return;
31747             }
31748             
31749             file.target.remove();
31750
31751         }, this);
31752         
31753         this.files = [];
31754         
31755         this.refresh();
31756     },
31757     
31758     onClick : function(e, el, o)
31759     {
31760         e.preventDefault();
31761         
31762         this.fireEvent('click', this, o);
31763         
31764     },
31765     
31766     closable : function(closable)
31767     {
31768         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31769             
31770             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31771             
31772             if(closable){
31773                 el.show();
31774                 return;
31775             }
31776             
31777             el.hide();
31778             
31779         }, this);
31780     },
31781     
31782     xhrOnLoad : function(xhr)
31783     {
31784         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31785             el.remove();
31786         }, this);
31787         
31788         if (xhr.readyState !== 4) {
31789             this.arrange();
31790             this.fireEvent('exception', this, xhr);
31791             return;
31792         }
31793
31794         var response = Roo.decode(xhr.responseText);
31795         
31796         if(!response.success){
31797             this.arrange();
31798             this.fireEvent('exception', this, xhr);
31799             return;
31800         }
31801         
31802         var file = this.renderPreview(response.data);
31803         
31804         this.files.push(file);
31805         
31806         this.arrange();
31807         
31808         this.fireEvent('afterupload', this, xhr);
31809         
31810     },
31811     
31812     xhrOnError : function(xhr)
31813     {
31814         Roo.log('xhr on error');
31815         
31816         var response = Roo.decode(xhr.responseText);
31817           
31818         Roo.log(response);
31819         
31820         this.arrange();
31821     },
31822     
31823     process : function(file)
31824     {
31825         if(this.fireEvent('process', this, file) !== false){
31826             if(this.editable && file.type.indexOf('image') != -1){
31827                 this.fireEvent('edit', this, file);
31828                 return;
31829             }
31830
31831             this.uploadStart(file, false);
31832
31833             return;
31834         }
31835         
31836     },
31837     
31838     uploadStart : function(file, crop)
31839     {
31840         this.xhr = new XMLHttpRequest();
31841         
31842         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31843             this.arrange();
31844             return;
31845         }
31846         
31847         file.xhr = this.xhr;
31848             
31849         this.managerEl.createChild({
31850             tag : 'div',
31851             cls : 'roo-document-manager-loading',
31852             cn : [
31853                 {
31854                     tag : 'div',
31855                     tooltip : file.name,
31856                     cls : 'roo-document-manager-thumb',
31857                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31858                 }
31859             ]
31860
31861         });
31862
31863         this.xhr.open(this.method, this.url, true);
31864         
31865         var headers = {
31866             "Accept": "application/json",
31867             "Cache-Control": "no-cache",
31868             "X-Requested-With": "XMLHttpRequest"
31869         };
31870         
31871         for (var headerName in headers) {
31872             var headerValue = headers[headerName];
31873             if (headerValue) {
31874                 this.xhr.setRequestHeader(headerName, headerValue);
31875             }
31876         }
31877         
31878         var _this = this;
31879         
31880         this.xhr.onload = function()
31881         {
31882             _this.xhrOnLoad(_this.xhr);
31883         }
31884         
31885         this.xhr.onerror = function()
31886         {
31887             _this.xhrOnError(_this.xhr);
31888         }
31889         
31890         var formData = new FormData();
31891
31892         formData.append('returnHTML', 'NO');
31893         
31894         if(crop){
31895             formData.append('crop', crop);
31896         }
31897         
31898         formData.append(this.paramName, file, file.name);
31899         
31900         var options = {
31901             file : file, 
31902             manually : false
31903         };
31904         
31905         if(this.fireEvent('prepare', this, formData, options) != false){
31906             
31907             if(options.manually){
31908                 return;
31909             }
31910             
31911             this.xhr.send(formData);
31912             return;
31913         };
31914         
31915         this.uploadCancel();
31916     },
31917     
31918     uploadCancel : function()
31919     {
31920         if (this.xhr) {
31921             this.xhr.abort();
31922         }
31923         
31924         this.delegates = [];
31925         
31926         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31927             el.remove();
31928         }, this);
31929         
31930         this.arrange();
31931     },
31932     
31933     renderPreview : function(file)
31934     {
31935         if(typeof(file.target) != 'undefined' && file.target){
31936             return file;
31937         }
31938         
31939         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31940         
31941         var previewEl = this.managerEl.createChild({
31942             tag : 'div',
31943             cls : 'roo-document-manager-preview',
31944             cn : [
31945                 {
31946                     tag : 'div',
31947                     tooltip : file[this.toolTipName],
31948                     cls : 'roo-document-manager-thumb',
31949                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31950                 },
31951                 {
31952                     tag : 'button',
31953                     cls : 'close',
31954                     html : '<i class="fa fa-times-circle"></i>'
31955                 }
31956             ]
31957         });
31958
31959         var close = previewEl.select('button.close', true).first();
31960
31961         close.on('click', this.onRemove, this, file);
31962
31963         file.target = previewEl;
31964
31965         var image = previewEl.select('img', true).first();
31966         
31967         var _this = this;
31968         
31969         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31970         
31971         image.on('click', this.onClick, this, file);
31972         
31973         this.fireEvent('previewrendered', this, file);
31974         
31975         return file;
31976         
31977     },
31978     
31979     onPreviewLoad : function(file, image)
31980     {
31981         if(typeof(file.target) == 'undefined' || !file.target){
31982             return;
31983         }
31984         
31985         var width = image.dom.naturalWidth || image.dom.width;
31986         var height = image.dom.naturalHeight || image.dom.height;
31987         
31988         if(!this.previewResize) {
31989             return;
31990         }
31991         
31992         if(width > height){
31993             file.target.addClass('wide');
31994             return;
31995         }
31996         
31997         file.target.addClass('tall');
31998         return;
31999         
32000     },
32001     
32002     uploadFromSource : function(file, crop)
32003     {
32004         this.xhr = new XMLHttpRequest();
32005         
32006         this.managerEl.createChild({
32007             tag : 'div',
32008             cls : 'roo-document-manager-loading',
32009             cn : [
32010                 {
32011                     tag : 'div',
32012                     tooltip : file.name,
32013                     cls : 'roo-document-manager-thumb',
32014                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32015                 }
32016             ]
32017
32018         });
32019
32020         this.xhr.open(this.method, this.url, true);
32021         
32022         var headers = {
32023             "Accept": "application/json",
32024             "Cache-Control": "no-cache",
32025             "X-Requested-With": "XMLHttpRequest"
32026         };
32027         
32028         for (var headerName in headers) {
32029             var headerValue = headers[headerName];
32030             if (headerValue) {
32031                 this.xhr.setRequestHeader(headerName, headerValue);
32032             }
32033         }
32034         
32035         var _this = this;
32036         
32037         this.xhr.onload = function()
32038         {
32039             _this.xhrOnLoad(_this.xhr);
32040         }
32041         
32042         this.xhr.onerror = function()
32043         {
32044             _this.xhrOnError(_this.xhr);
32045         }
32046         
32047         var formData = new FormData();
32048
32049         formData.append('returnHTML', 'NO');
32050         
32051         formData.append('crop', crop);
32052         
32053         if(typeof(file.filename) != 'undefined'){
32054             formData.append('filename', file.filename);
32055         }
32056         
32057         if(typeof(file.mimetype) != 'undefined'){
32058             formData.append('mimetype', file.mimetype);
32059         }
32060         
32061         Roo.log(formData);
32062         
32063         if(this.fireEvent('prepare', this, formData) != false){
32064             this.xhr.send(formData);
32065         };
32066     }
32067 });
32068
32069 /*
32070 * Licence: LGPL
32071 */
32072
32073 /**
32074  * @class Roo.bootstrap.DocumentViewer
32075  * @extends Roo.bootstrap.Component
32076  * Bootstrap DocumentViewer class
32077  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32078  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32079  * 
32080  * @constructor
32081  * Create a new DocumentViewer
32082  * @param {Object} config The config object
32083  */
32084
32085 Roo.bootstrap.DocumentViewer = function(config){
32086     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32087     
32088     this.addEvents({
32089         /**
32090          * @event initial
32091          * Fire after initEvent
32092          * @param {Roo.bootstrap.DocumentViewer} this
32093          */
32094         "initial" : true,
32095         /**
32096          * @event click
32097          * Fire after click
32098          * @param {Roo.bootstrap.DocumentViewer} this
32099          */
32100         "click" : true,
32101         /**
32102          * @event download
32103          * Fire after download button
32104          * @param {Roo.bootstrap.DocumentViewer} this
32105          */
32106         "download" : true,
32107         /**
32108          * @event trash
32109          * Fire after trash button
32110          * @param {Roo.bootstrap.DocumentViewer} this
32111          */
32112         "trash" : true
32113         
32114     });
32115 };
32116
32117 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32118     
32119     showDownload : true,
32120     
32121     showTrash : true,
32122     
32123     getAutoCreate : function()
32124     {
32125         var cfg = {
32126             tag : 'div',
32127             cls : 'roo-document-viewer',
32128             cn : [
32129                 {
32130                     tag : 'div',
32131                     cls : 'roo-document-viewer-body',
32132                     cn : [
32133                         {
32134                             tag : 'div',
32135                             cls : 'roo-document-viewer-thumb',
32136                             cn : [
32137                                 {
32138                                     tag : 'img',
32139                                     cls : 'roo-document-viewer-image'
32140                                 }
32141                             ]
32142                         }
32143                     ]
32144                 },
32145                 {
32146                     tag : 'div',
32147                     cls : 'roo-document-viewer-footer',
32148                     cn : {
32149                         tag : 'div',
32150                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32151                         cn : [
32152                             {
32153                                 tag : 'div',
32154                                 cls : 'btn-group roo-document-viewer-download',
32155                                 cn : [
32156                                     {
32157                                         tag : 'button',
32158                                         cls : 'btn btn-default',
32159                                         html : '<i class="fa fa-download"></i>'
32160                                     }
32161                                 ]
32162                             },
32163                             {
32164                                 tag : 'div',
32165                                 cls : 'btn-group roo-document-viewer-trash',
32166                                 cn : [
32167                                     {
32168                                         tag : 'button',
32169                                         cls : 'btn btn-default',
32170                                         html : '<i class="fa fa-trash"></i>'
32171                                     }
32172                                 ]
32173                             }
32174                         ]
32175                     }
32176                 }
32177             ]
32178         };
32179         
32180         return cfg;
32181     },
32182     
32183     initEvents : function()
32184     {
32185         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32186         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32187         
32188         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32189         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32190         
32191         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32192         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32193         
32194         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32195         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32196         
32197         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32198         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32199         
32200         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32201         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32202         
32203         this.bodyEl.on('click', this.onClick, this);
32204         this.downloadBtn.on('click', this.onDownload, this);
32205         this.trashBtn.on('click', this.onTrash, this);
32206         
32207         this.downloadBtn.hide();
32208         this.trashBtn.hide();
32209         
32210         if(this.showDownload){
32211             this.downloadBtn.show();
32212         }
32213         
32214         if(this.showTrash){
32215             this.trashBtn.show();
32216         }
32217         
32218         if(!this.showDownload && !this.showTrash) {
32219             this.footerEl.hide();
32220         }
32221         
32222     },
32223     
32224     initial : function()
32225     {
32226         this.fireEvent('initial', this);
32227         
32228     },
32229     
32230     onClick : function(e)
32231     {
32232         e.preventDefault();
32233         
32234         this.fireEvent('click', this);
32235     },
32236     
32237     onDownload : function(e)
32238     {
32239         e.preventDefault();
32240         
32241         this.fireEvent('download', this);
32242     },
32243     
32244     onTrash : function(e)
32245     {
32246         e.preventDefault();
32247         
32248         this.fireEvent('trash', this);
32249     }
32250     
32251 });
32252 /*
32253  * - LGPL
32254  *
32255  * nav progress bar
32256  * 
32257  */
32258
32259 /**
32260  * @class Roo.bootstrap.NavProgressBar
32261  * @extends Roo.bootstrap.Component
32262  * Bootstrap NavProgressBar class
32263  * 
32264  * @constructor
32265  * Create a new nav progress bar
32266  * @param {Object} config The config object
32267  */
32268
32269 Roo.bootstrap.NavProgressBar = function(config){
32270     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32271
32272     this.bullets = this.bullets || [];
32273    
32274 //    Roo.bootstrap.NavProgressBar.register(this);
32275      this.addEvents({
32276         /**
32277              * @event changed
32278              * Fires when the active item changes
32279              * @param {Roo.bootstrap.NavProgressBar} this
32280              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32281              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32282          */
32283         'changed': true
32284      });
32285     
32286 };
32287
32288 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32289     
32290     bullets : [],
32291     barItems : [],
32292     
32293     getAutoCreate : function()
32294     {
32295         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32296         
32297         cfg = {
32298             tag : 'div',
32299             cls : 'roo-navigation-bar-group',
32300             cn : [
32301                 {
32302                     tag : 'div',
32303                     cls : 'roo-navigation-top-bar'
32304                 },
32305                 {
32306                     tag : 'div',
32307                     cls : 'roo-navigation-bullets-bar',
32308                     cn : [
32309                         {
32310                             tag : 'ul',
32311                             cls : 'roo-navigation-bar'
32312                         }
32313                     ]
32314                 },
32315                 
32316                 {
32317                     tag : 'div',
32318                     cls : 'roo-navigation-bottom-bar'
32319                 }
32320             ]
32321             
32322         };
32323         
32324         return cfg;
32325         
32326     },
32327     
32328     initEvents: function() 
32329     {
32330         
32331     },
32332     
32333     onRender : function(ct, position) 
32334     {
32335         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32336         
32337         if(this.bullets.length){
32338             Roo.each(this.bullets, function(b){
32339                this.addItem(b);
32340             }, this);
32341         }
32342         
32343         this.format();
32344         
32345     },
32346     
32347     addItem : function(cfg)
32348     {
32349         var item = new Roo.bootstrap.NavProgressItem(cfg);
32350         
32351         item.parentId = this.id;
32352         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32353         
32354         if(cfg.html){
32355             var top = new Roo.bootstrap.Element({
32356                 tag : 'div',
32357                 cls : 'roo-navigation-bar-text'
32358             });
32359             
32360             var bottom = new Roo.bootstrap.Element({
32361                 tag : 'div',
32362                 cls : 'roo-navigation-bar-text'
32363             });
32364             
32365             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32366             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32367             
32368             var topText = new Roo.bootstrap.Element({
32369                 tag : 'span',
32370                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32371             });
32372             
32373             var bottomText = new Roo.bootstrap.Element({
32374                 tag : 'span',
32375                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32376             });
32377             
32378             topText.onRender(top.el, null);
32379             bottomText.onRender(bottom.el, null);
32380             
32381             item.topEl = top;
32382             item.bottomEl = bottom;
32383         }
32384         
32385         this.barItems.push(item);
32386         
32387         return item;
32388     },
32389     
32390     getActive : function()
32391     {
32392         var active = false;
32393         
32394         Roo.each(this.barItems, function(v){
32395             
32396             if (!v.isActive()) {
32397                 return;
32398             }
32399             
32400             active = v;
32401             return false;
32402             
32403         });
32404         
32405         return active;
32406     },
32407     
32408     setActiveItem : function(item)
32409     {
32410         var prev = false;
32411         
32412         Roo.each(this.barItems, function(v){
32413             if (v.rid == item.rid) {
32414                 return ;
32415             }
32416             
32417             if (v.isActive()) {
32418                 v.setActive(false);
32419                 prev = v;
32420             }
32421         });
32422
32423         item.setActive(true);
32424         
32425         this.fireEvent('changed', this, item, prev);
32426     },
32427     
32428     getBarItem: function(rid)
32429     {
32430         var ret = false;
32431         
32432         Roo.each(this.barItems, function(e) {
32433             if (e.rid != rid) {
32434                 return;
32435             }
32436             
32437             ret =  e;
32438             return false;
32439         });
32440         
32441         return ret;
32442     },
32443     
32444     indexOfItem : function(item)
32445     {
32446         var index = false;
32447         
32448         Roo.each(this.barItems, function(v, i){
32449             
32450             if (v.rid != item.rid) {
32451                 return;
32452             }
32453             
32454             index = i;
32455             return false
32456         });
32457         
32458         return index;
32459     },
32460     
32461     setActiveNext : function()
32462     {
32463         var i = this.indexOfItem(this.getActive());
32464         
32465         if (i > this.barItems.length) {
32466             return;
32467         }
32468         
32469         this.setActiveItem(this.barItems[i+1]);
32470     },
32471     
32472     setActivePrev : function()
32473     {
32474         var i = this.indexOfItem(this.getActive());
32475         
32476         if (i  < 1) {
32477             return;
32478         }
32479         
32480         this.setActiveItem(this.barItems[i-1]);
32481     },
32482     
32483     format : function()
32484     {
32485         if(!this.barItems.length){
32486             return;
32487         }
32488      
32489         var width = 100 / this.barItems.length;
32490         
32491         Roo.each(this.barItems, function(i){
32492             i.el.setStyle('width', width + '%');
32493             i.topEl.el.setStyle('width', width + '%');
32494             i.bottomEl.el.setStyle('width', width + '%');
32495         }, this);
32496         
32497     }
32498     
32499 });
32500 /*
32501  * - LGPL
32502  *
32503  * Nav Progress Item
32504  * 
32505  */
32506
32507 /**
32508  * @class Roo.bootstrap.NavProgressItem
32509  * @extends Roo.bootstrap.Component
32510  * Bootstrap NavProgressItem class
32511  * @cfg {String} rid the reference id
32512  * @cfg {Boolean} active (true|false) Is item active default false
32513  * @cfg {Boolean} disabled (true|false) Is item active default false
32514  * @cfg {String} html
32515  * @cfg {String} position (top|bottom) text position default bottom
32516  * @cfg {String} icon show icon instead of number
32517  * 
32518  * @constructor
32519  * Create a new NavProgressItem
32520  * @param {Object} config The config object
32521  */
32522 Roo.bootstrap.NavProgressItem = function(config){
32523     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32524     this.addEvents({
32525         // raw events
32526         /**
32527          * @event click
32528          * The raw click event for the entire grid.
32529          * @param {Roo.bootstrap.NavProgressItem} this
32530          * @param {Roo.EventObject} e
32531          */
32532         "click" : true
32533     });
32534    
32535 };
32536
32537 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32538     
32539     rid : '',
32540     active : false,
32541     disabled : false,
32542     html : '',
32543     position : 'bottom',
32544     icon : false,
32545     
32546     getAutoCreate : function()
32547     {
32548         var iconCls = 'roo-navigation-bar-item-icon';
32549         
32550         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32551         
32552         var cfg = {
32553             tag: 'li',
32554             cls: 'roo-navigation-bar-item',
32555             cn : [
32556                 {
32557                     tag : 'i',
32558                     cls : iconCls
32559                 }
32560             ]
32561         };
32562         
32563         if(this.active){
32564             cfg.cls += ' active';
32565         }
32566         if(this.disabled){
32567             cfg.cls += ' disabled';
32568         }
32569         
32570         return cfg;
32571     },
32572     
32573     disable : function()
32574     {
32575         this.setDisabled(true);
32576     },
32577     
32578     enable : function()
32579     {
32580         this.setDisabled(false);
32581     },
32582     
32583     initEvents: function() 
32584     {
32585         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32586         
32587         this.iconEl.on('click', this.onClick, this);
32588     },
32589     
32590     onClick : function(e)
32591     {
32592         e.preventDefault();
32593         
32594         if(this.disabled){
32595             return;
32596         }
32597         
32598         if(this.fireEvent('click', this, e) === false){
32599             return;
32600         };
32601         
32602         this.parent().setActiveItem(this);
32603     },
32604     
32605     isActive: function () 
32606     {
32607         return this.active;
32608     },
32609     
32610     setActive : function(state)
32611     {
32612         if(this.active == state){
32613             return;
32614         }
32615         
32616         this.active = state;
32617         
32618         if (state) {
32619             this.el.addClass('active');
32620             return;
32621         }
32622         
32623         this.el.removeClass('active');
32624         
32625         return;
32626     },
32627     
32628     setDisabled : function(state)
32629     {
32630         if(this.disabled == state){
32631             return;
32632         }
32633         
32634         this.disabled = state;
32635         
32636         if (state) {
32637             this.el.addClass('disabled');
32638             return;
32639         }
32640         
32641         this.el.removeClass('disabled');
32642     },
32643     
32644     tooltipEl : function()
32645     {
32646         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32647     }
32648 });
32649  
32650
32651  /*
32652  * - LGPL
32653  *
32654  * FieldLabel
32655  * 
32656  */
32657
32658 /**
32659  * @class Roo.bootstrap.FieldLabel
32660  * @extends Roo.bootstrap.Component
32661  * Bootstrap FieldLabel class
32662  * @cfg {String} html contents of the element
32663  * @cfg {String} tag tag of the element default label
32664  * @cfg {String} cls class of the element
32665  * @cfg {String} target label target 
32666  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32667  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32668  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32669  * @cfg {String} iconTooltip default "This field is required"
32670  * @cfg {String} indicatorpos (left|right) default left
32671  * 
32672  * @constructor
32673  * Create a new FieldLabel
32674  * @param {Object} config The config object
32675  */
32676
32677 Roo.bootstrap.FieldLabel = function(config){
32678     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32679     
32680     this.addEvents({
32681             /**
32682              * @event invalid
32683              * Fires after the field has been marked as invalid.
32684              * @param {Roo.form.FieldLabel} this
32685              * @param {String} msg The validation message
32686              */
32687             invalid : true,
32688             /**
32689              * @event valid
32690              * Fires after the field has been validated with no errors.
32691              * @param {Roo.form.FieldLabel} this
32692              */
32693             valid : true
32694         });
32695 };
32696
32697 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32698     
32699     tag: 'label',
32700     cls: '',
32701     html: '',
32702     target: '',
32703     allowBlank : true,
32704     invalidClass : 'has-warning',
32705     validClass : 'has-success',
32706     iconTooltip : 'This field is required',
32707     indicatorpos : 'left',
32708     
32709     getAutoCreate : function(){
32710         
32711         var cls = "";
32712         if (!this.allowBlank) {
32713             cls  = "visible";
32714         }
32715         
32716         var cfg = {
32717             tag : this.tag,
32718             cls : 'roo-bootstrap-field-label ' + this.cls,
32719             for : this.target,
32720             cn : [
32721                 {
32722                     tag : 'i',
32723                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32724                     tooltip : this.iconTooltip
32725                 },
32726                 {
32727                     tag : 'span',
32728                     html : this.html
32729                 }
32730             ] 
32731         };
32732         
32733         if(this.indicatorpos == 'right'){
32734             var cfg = {
32735                 tag : this.tag,
32736                 cls : 'roo-bootstrap-field-label ' + this.cls,
32737                 for : this.target,
32738                 cn : [
32739                     {
32740                         tag : 'span',
32741                         html : this.html
32742                     },
32743                     {
32744                         tag : 'i',
32745                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32746                         tooltip : this.iconTooltip
32747                     }
32748                 ] 
32749             };
32750         }
32751         
32752         return cfg;
32753     },
32754     
32755     initEvents: function() 
32756     {
32757         Roo.bootstrap.Element.superclass.initEvents.call(this);
32758         
32759         this.indicator = this.indicatorEl();
32760         
32761         if(this.indicator){
32762             this.indicator.removeClass('visible');
32763             this.indicator.addClass('invisible');
32764         }
32765         
32766         Roo.bootstrap.FieldLabel.register(this);
32767     },
32768     
32769     indicatorEl : function()
32770     {
32771         var indicator = this.el.select('i.roo-required-indicator',true).first();
32772         
32773         if(!indicator){
32774             return false;
32775         }
32776         
32777         return indicator;
32778         
32779     },
32780     
32781     /**
32782      * Mark this field as valid
32783      */
32784     markValid : function()
32785     {
32786         if(this.indicator){
32787             this.indicator.removeClass('visible');
32788             this.indicator.addClass('invisible');
32789         }
32790         if (Roo.bootstrap.version == 3) {
32791             this.el.removeClass(this.invalidClass);
32792             this.el.addClass(this.validClass);
32793         } else {
32794             this.el.removeClass('is-invalid');
32795             this.el.addClass('is-valid');
32796         }
32797         
32798         
32799         this.fireEvent('valid', this);
32800     },
32801     
32802     /**
32803      * Mark this field as invalid
32804      * @param {String} msg The validation message
32805      */
32806     markInvalid : function(msg)
32807     {
32808         if(this.indicator){
32809             this.indicator.removeClass('invisible');
32810             this.indicator.addClass('visible');
32811         }
32812           if (Roo.bootstrap.version == 3) {
32813             this.el.removeClass(this.validClass);
32814             this.el.addClass(this.invalidClass);
32815         } else {
32816             this.el.removeClass('is-valid');
32817             this.el.addClass('is-invalid');
32818         }
32819         
32820         
32821         this.fireEvent('invalid', this, msg);
32822     }
32823     
32824    
32825 });
32826
32827 Roo.apply(Roo.bootstrap.FieldLabel, {
32828     
32829     groups: {},
32830     
32831      /**
32832     * register a FieldLabel Group
32833     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32834     */
32835     register : function(label)
32836     {
32837         if(this.groups.hasOwnProperty(label.target)){
32838             return;
32839         }
32840      
32841         this.groups[label.target] = label;
32842         
32843     },
32844     /**
32845     * fetch a FieldLabel Group based on the target
32846     * @param {string} target
32847     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32848     */
32849     get: function(target) {
32850         if (typeof(this.groups[target]) == 'undefined') {
32851             return false;
32852         }
32853         
32854         return this.groups[target] ;
32855     }
32856 });
32857
32858  
32859
32860  /*
32861  * - LGPL
32862  *
32863  * page DateSplitField.
32864  * 
32865  */
32866
32867
32868 /**
32869  * @class Roo.bootstrap.DateSplitField
32870  * @extends Roo.bootstrap.Component
32871  * Bootstrap DateSplitField class
32872  * @cfg {string} fieldLabel - the label associated
32873  * @cfg {Number} labelWidth set the width of label (0-12)
32874  * @cfg {String} labelAlign (top|left)
32875  * @cfg {Boolean} dayAllowBlank (true|false) default false
32876  * @cfg {Boolean} monthAllowBlank (true|false) default false
32877  * @cfg {Boolean} yearAllowBlank (true|false) default false
32878  * @cfg {string} dayPlaceholder 
32879  * @cfg {string} monthPlaceholder
32880  * @cfg {string} yearPlaceholder
32881  * @cfg {string} dayFormat default 'd'
32882  * @cfg {string} monthFormat default 'm'
32883  * @cfg {string} yearFormat default 'Y'
32884  * @cfg {Number} labellg set the width of label (1-12)
32885  * @cfg {Number} labelmd set the width of label (1-12)
32886  * @cfg {Number} labelsm set the width of label (1-12)
32887  * @cfg {Number} labelxs set the width of label (1-12)
32888
32889  *     
32890  * @constructor
32891  * Create a new DateSplitField
32892  * @param {Object} config The config object
32893  */
32894
32895 Roo.bootstrap.DateSplitField = function(config){
32896     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32897     
32898     this.addEvents({
32899         // raw events
32900          /**
32901          * @event years
32902          * getting the data of years
32903          * @param {Roo.bootstrap.DateSplitField} this
32904          * @param {Object} years
32905          */
32906         "years" : true,
32907         /**
32908          * @event days
32909          * getting the data of days
32910          * @param {Roo.bootstrap.DateSplitField} this
32911          * @param {Object} days
32912          */
32913         "days" : true,
32914         /**
32915          * @event invalid
32916          * Fires after the field has been marked as invalid.
32917          * @param {Roo.form.Field} this
32918          * @param {String} msg The validation message
32919          */
32920         invalid : true,
32921        /**
32922          * @event valid
32923          * Fires after the field has been validated with no errors.
32924          * @param {Roo.form.Field} this
32925          */
32926         valid : true
32927     });
32928 };
32929
32930 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32931     
32932     fieldLabel : '',
32933     labelAlign : 'top',
32934     labelWidth : 3,
32935     dayAllowBlank : false,
32936     monthAllowBlank : false,
32937     yearAllowBlank : false,
32938     dayPlaceholder : '',
32939     monthPlaceholder : '',
32940     yearPlaceholder : '',
32941     dayFormat : 'd',
32942     monthFormat : 'm',
32943     yearFormat : 'Y',
32944     isFormField : true,
32945     labellg : 0,
32946     labelmd : 0,
32947     labelsm : 0,
32948     labelxs : 0,
32949     
32950     getAutoCreate : function()
32951     {
32952         var cfg = {
32953             tag : 'div',
32954             cls : 'row roo-date-split-field-group',
32955             cn : [
32956                 {
32957                     tag : 'input',
32958                     type : 'hidden',
32959                     cls : 'form-hidden-field roo-date-split-field-group-value',
32960                     name : this.name
32961                 }
32962             ]
32963         };
32964         
32965         var labelCls = 'col-md-12';
32966         var contentCls = 'col-md-4';
32967         
32968         if(this.fieldLabel){
32969             
32970             var label = {
32971                 tag : 'div',
32972                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32973                 cn : [
32974                     {
32975                         tag : 'label',
32976                         html : this.fieldLabel
32977                     }
32978                 ]
32979             };
32980             
32981             if(this.labelAlign == 'left'){
32982             
32983                 if(this.labelWidth > 12){
32984                     label.style = "width: " + this.labelWidth + 'px';
32985                 }
32986
32987                 if(this.labelWidth < 13 && this.labelmd == 0){
32988                     this.labelmd = this.labelWidth;
32989                 }
32990
32991                 if(this.labellg > 0){
32992                     labelCls = ' col-lg-' + this.labellg;
32993                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32994                 }
32995
32996                 if(this.labelmd > 0){
32997                     labelCls = ' col-md-' + this.labelmd;
32998                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32999                 }
33000
33001                 if(this.labelsm > 0){
33002                     labelCls = ' col-sm-' + this.labelsm;
33003                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33004                 }
33005
33006                 if(this.labelxs > 0){
33007                     labelCls = ' col-xs-' + this.labelxs;
33008                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33009                 }
33010             }
33011             
33012             label.cls += ' ' + labelCls;
33013             
33014             cfg.cn.push(label);
33015         }
33016         
33017         Roo.each(['day', 'month', 'year'], function(t){
33018             cfg.cn.push({
33019                 tag : 'div',
33020                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33021             });
33022         }, this);
33023         
33024         return cfg;
33025     },
33026     
33027     inputEl: function ()
33028     {
33029         return this.el.select('.roo-date-split-field-group-value', true).first();
33030     },
33031     
33032     onRender : function(ct, position) 
33033     {
33034         var _this = this;
33035         
33036         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33037         
33038         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33039         
33040         this.dayField = new Roo.bootstrap.ComboBox({
33041             allowBlank : this.dayAllowBlank,
33042             alwaysQuery : true,
33043             displayField : 'value',
33044             editable : false,
33045             fieldLabel : '',
33046             forceSelection : true,
33047             mode : 'local',
33048             placeholder : this.dayPlaceholder,
33049             selectOnFocus : true,
33050             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33051             triggerAction : 'all',
33052             typeAhead : true,
33053             valueField : 'value',
33054             store : new Roo.data.SimpleStore({
33055                 data : (function() {    
33056                     var days = [];
33057                     _this.fireEvent('days', _this, days);
33058                     return days;
33059                 })(),
33060                 fields : [ 'value' ]
33061             }),
33062             listeners : {
33063                 select : function (_self, record, index)
33064                 {
33065                     _this.setValue(_this.getValue());
33066                 }
33067             }
33068         });
33069
33070         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33071         
33072         this.monthField = new Roo.bootstrap.MonthField({
33073             after : '<i class=\"fa fa-calendar\"></i>',
33074             allowBlank : this.monthAllowBlank,
33075             placeholder : this.monthPlaceholder,
33076             readOnly : true,
33077             listeners : {
33078                 render : function (_self)
33079                 {
33080                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33081                         e.preventDefault();
33082                         _self.focus();
33083                     });
33084                 },
33085                 select : function (_self, oldvalue, newvalue)
33086                 {
33087                     _this.setValue(_this.getValue());
33088                 }
33089             }
33090         });
33091         
33092         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33093         
33094         this.yearField = new Roo.bootstrap.ComboBox({
33095             allowBlank : this.yearAllowBlank,
33096             alwaysQuery : true,
33097             displayField : 'value',
33098             editable : false,
33099             fieldLabel : '',
33100             forceSelection : true,
33101             mode : 'local',
33102             placeholder : this.yearPlaceholder,
33103             selectOnFocus : true,
33104             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33105             triggerAction : 'all',
33106             typeAhead : true,
33107             valueField : 'value',
33108             store : new Roo.data.SimpleStore({
33109                 data : (function() {
33110                     var years = [];
33111                     _this.fireEvent('years', _this, years);
33112                     return years;
33113                 })(),
33114                 fields : [ 'value' ]
33115             }),
33116             listeners : {
33117                 select : function (_self, record, index)
33118                 {
33119                     _this.setValue(_this.getValue());
33120                 }
33121             }
33122         });
33123
33124         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33125     },
33126     
33127     setValue : function(v, format)
33128     {
33129         this.inputEl.dom.value = v;
33130         
33131         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33132         
33133         var d = Date.parseDate(v, f);
33134         
33135         if(!d){
33136             this.validate();
33137             return;
33138         }
33139         
33140         this.setDay(d.format(this.dayFormat));
33141         this.setMonth(d.format(this.monthFormat));
33142         this.setYear(d.format(this.yearFormat));
33143         
33144         this.validate();
33145         
33146         return;
33147     },
33148     
33149     setDay : function(v)
33150     {
33151         this.dayField.setValue(v);
33152         this.inputEl.dom.value = this.getValue();
33153         this.validate();
33154         return;
33155     },
33156     
33157     setMonth : function(v)
33158     {
33159         this.monthField.setValue(v, true);
33160         this.inputEl.dom.value = this.getValue();
33161         this.validate();
33162         return;
33163     },
33164     
33165     setYear : function(v)
33166     {
33167         this.yearField.setValue(v);
33168         this.inputEl.dom.value = this.getValue();
33169         this.validate();
33170         return;
33171     },
33172     
33173     getDay : function()
33174     {
33175         return this.dayField.getValue();
33176     },
33177     
33178     getMonth : function()
33179     {
33180         return this.monthField.getValue();
33181     },
33182     
33183     getYear : function()
33184     {
33185         return this.yearField.getValue();
33186     },
33187     
33188     getValue : function()
33189     {
33190         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33191         
33192         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33193         
33194         return date;
33195     },
33196     
33197     reset : function()
33198     {
33199         this.setDay('');
33200         this.setMonth('');
33201         this.setYear('');
33202         this.inputEl.dom.value = '';
33203         this.validate();
33204         return;
33205     },
33206     
33207     validate : function()
33208     {
33209         var d = this.dayField.validate();
33210         var m = this.monthField.validate();
33211         var y = this.yearField.validate();
33212         
33213         var valid = true;
33214         
33215         if(
33216                 (!this.dayAllowBlank && !d) ||
33217                 (!this.monthAllowBlank && !m) ||
33218                 (!this.yearAllowBlank && !y)
33219         ){
33220             valid = false;
33221         }
33222         
33223         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33224             return valid;
33225         }
33226         
33227         if(valid){
33228             this.markValid();
33229             return valid;
33230         }
33231         
33232         this.markInvalid();
33233         
33234         return valid;
33235     },
33236     
33237     markValid : function()
33238     {
33239         
33240         var label = this.el.select('label', true).first();
33241         var icon = this.el.select('i.fa-star', true).first();
33242
33243         if(label && icon){
33244             icon.remove();
33245         }
33246         
33247         this.fireEvent('valid', this);
33248     },
33249     
33250      /**
33251      * Mark this field as invalid
33252      * @param {String} msg The validation message
33253      */
33254     markInvalid : function(msg)
33255     {
33256         
33257         var label = this.el.select('label', true).first();
33258         var icon = this.el.select('i.fa-star', true).first();
33259
33260         if(label && !icon){
33261             this.el.select('.roo-date-split-field-label', true).createChild({
33262                 tag : 'i',
33263                 cls : 'text-danger fa fa-lg fa-star',
33264                 tooltip : 'This field is required',
33265                 style : 'margin-right:5px;'
33266             }, label, true);
33267         }
33268         
33269         this.fireEvent('invalid', this, msg);
33270     },
33271     
33272     clearInvalid : function()
33273     {
33274         var label = this.el.select('label', true).first();
33275         var icon = this.el.select('i.fa-star', true).first();
33276
33277         if(label && icon){
33278             icon.remove();
33279         }
33280         
33281         this.fireEvent('valid', this);
33282     },
33283     
33284     getName: function()
33285     {
33286         return this.name;
33287     }
33288     
33289 });
33290
33291  /**
33292  *
33293  * This is based on 
33294  * http://masonry.desandro.com
33295  *
33296  * The idea is to render all the bricks based on vertical width...
33297  *
33298  * The original code extends 'outlayer' - we might need to use that....
33299  * 
33300  */
33301
33302
33303 /**
33304  * @class Roo.bootstrap.LayoutMasonry
33305  * @extends Roo.bootstrap.Component
33306  * Bootstrap Layout Masonry class
33307  * 
33308  * @constructor
33309  * Create a new Element
33310  * @param {Object} config The config object
33311  */
33312
33313 Roo.bootstrap.LayoutMasonry = function(config){
33314     
33315     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33316     
33317     this.bricks = [];
33318     
33319     Roo.bootstrap.LayoutMasonry.register(this);
33320     
33321     this.addEvents({
33322         // raw events
33323         /**
33324          * @event layout
33325          * Fire after layout the items
33326          * @param {Roo.bootstrap.LayoutMasonry} this
33327          * @param {Roo.EventObject} e
33328          */
33329         "layout" : true
33330     });
33331     
33332 };
33333
33334 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33335     
33336     /**
33337      * @cfg {Boolean} isLayoutInstant = no animation?
33338      */   
33339     isLayoutInstant : false, // needed?
33340    
33341     /**
33342      * @cfg {Number} boxWidth  width of the columns
33343      */   
33344     boxWidth : 450,
33345     
33346       /**
33347      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33348      */   
33349     boxHeight : 0,
33350     
33351     /**
33352      * @cfg {Number} padWidth padding below box..
33353      */   
33354     padWidth : 10, 
33355     
33356     /**
33357      * @cfg {Number} gutter gutter width..
33358      */   
33359     gutter : 10,
33360     
33361      /**
33362      * @cfg {Number} maxCols maximum number of columns
33363      */   
33364     
33365     maxCols: 0,
33366     
33367     /**
33368      * @cfg {Boolean} isAutoInitial defalut true
33369      */   
33370     isAutoInitial : true, 
33371     
33372     containerWidth: 0,
33373     
33374     /**
33375      * @cfg {Boolean} isHorizontal defalut false
33376      */   
33377     isHorizontal : false, 
33378
33379     currentSize : null,
33380     
33381     tag: 'div',
33382     
33383     cls: '',
33384     
33385     bricks: null, //CompositeElement
33386     
33387     cols : 1,
33388     
33389     _isLayoutInited : false,
33390     
33391 //    isAlternative : false, // only use for vertical layout...
33392     
33393     /**
33394      * @cfg {Number} alternativePadWidth padding below box..
33395      */   
33396     alternativePadWidth : 50,
33397     
33398     selectedBrick : [],
33399     
33400     getAutoCreate : function(){
33401         
33402         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33403         
33404         var cfg = {
33405             tag: this.tag,
33406             cls: 'blog-masonary-wrapper ' + this.cls,
33407             cn : {
33408                 cls : 'mas-boxes masonary'
33409             }
33410         };
33411         
33412         return cfg;
33413     },
33414     
33415     getChildContainer: function( )
33416     {
33417         if (this.boxesEl) {
33418             return this.boxesEl;
33419         }
33420         
33421         this.boxesEl = this.el.select('.mas-boxes').first();
33422         
33423         return this.boxesEl;
33424     },
33425     
33426     
33427     initEvents : function()
33428     {
33429         var _this = this;
33430         
33431         if(this.isAutoInitial){
33432             Roo.log('hook children rendered');
33433             this.on('childrenrendered', function() {
33434                 Roo.log('children rendered');
33435                 _this.initial();
33436             } ,this);
33437         }
33438     },
33439     
33440     initial : function()
33441     {
33442         this.selectedBrick = [];
33443         
33444         this.currentSize = this.el.getBox(true);
33445         
33446         Roo.EventManager.onWindowResize(this.resize, this); 
33447
33448         if(!this.isAutoInitial){
33449             this.layout();
33450             return;
33451         }
33452         
33453         this.layout();
33454         
33455         return;
33456         //this.layout.defer(500,this);
33457         
33458     },
33459     
33460     resize : function()
33461     {
33462         var cs = this.el.getBox(true);
33463         
33464         if (
33465                 this.currentSize.width == cs.width && 
33466                 this.currentSize.x == cs.x && 
33467                 this.currentSize.height == cs.height && 
33468                 this.currentSize.y == cs.y 
33469         ) {
33470             Roo.log("no change in with or X or Y");
33471             return;
33472         }
33473         
33474         this.currentSize = cs;
33475         
33476         this.layout();
33477         
33478     },
33479     
33480     layout : function()
33481     {   
33482         this._resetLayout();
33483         
33484         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33485         
33486         this.layoutItems( isInstant );
33487       
33488         this._isLayoutInited = true;
33489         
33490         this.fireEvent('layout', this);
33491         
33492     },
33493     
33494     _resetLayout : function()
33495     {
33496         if(this.isHorizontal){
33497             this.horizontalMeasureColumns();
33498             return;
33499         }
33500         
33501         this.verticalMeasureColumns();
33502         
33503     },
33504     
33505     verticalMeasureColumns : function()
33506     {
33507         this.getContainerWidth();
33508         
33509 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33510 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33511 //            return;
33512 //        }
33513         
33514         var boxWidth = this.boxWidth + this.padWidth;
33515         
33516         if(this.containerWidth < this.boxWidth){
33517             boxWidth = this.containerWidth
33518         }
33519         
33520         var containerWidth = this.containerWidth;
33521         
33522         var cols = Math.floor(containerWidth / boxWidth);
33523         
33524         this.cols = Math.max( cols, 1 );
33525         
33526         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33527         
33528         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33529         
33530         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33531         
33532         this.colWidth = boxWidth + avail - this.padWidth;
33533         
33534         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33535         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33536     },
33537     
33538     horizontalMeasureColumns : function()
33539     {
33540         this.getContainerWidth();
33541         
33542         var boxWidth = this.boxWidth;
33543         
33544         if(this.containerWidth < boxWidth){
33545             boxWidth = this.containerWidth;
33546         }
33547         
33548         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33549         
33550         this.el.setHeight(boxWidth);
33551         
33552     },
33553     
33554     getContainerWidth : function()
33555     {
33556         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33557     },
33558     
33559     layoutItems : function( isInstant )
33560     {
33561         Roo.log(this.bricks);
33562         
33563         var items = Roo.apply([], this.bricks);
33564         
33565         if(this.isHorizontal){
33566             this._horizontalLayoutItems( items , isInstant );
33567             return;
33568         }
33569         
33570 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33571 //            this._verticalAlternativeLayoutItems( items , isInstant );
33572 //            return;
33573 //        }
33574         
33575         this._verticalLayoutItems( items , isInstant );
33576         
33577     },
33578     
33579     _verticalLayoutItems : function ( items , isInstant)
33580     {
33581         if ( !items || !items.length ) {
33582             return;
33583         }
33584         
33585         var standard = [
33586             ['xs', 'xs', 'xs', 'tall'],
33587             ['xs', 'xs', 'tall'],
33588             ['xs', 'xs', 'sm'],
33589             ['xs', 'xs', 'xs'],
33590             ['xs', 'tall'],
33591             ['xs', 'sm'],
33592             ['xs', 'xs'],
33593             ['xs'],
33594             
33595             ['sm', 'xs', 'xs'],
33596             ['sm', 'xs'],
33597             ['sm'],
33598             
33599             ['tall', 'xs', 'xs', 'xs'],
33600             ['tall', 'xs', 'xs'],
33601             ['tall', 'xs'],
33602             ['tall']
33603             
33604         ];
33605         
33606         var queue = [];
33607         
33608         var boxes = [];
33609         
33610         var box = [];
33611         
33612         Roo.each(items, function(item, k){
33613             
33614             switch (item.size) {
33615                 // these layouts take up a full box,
33616                 case 'md' :
33617                 case 'md-left' :
33618                 case 'md-right' :
33619                 case 'wide' :
33620                     
33621                     if(box.length){
33622                         boxes.push(box);
33623                         box = [];
33624                     }
33625                     
33626                     boxes.push([item]);
33627                     
33628                     break;
33629                     
33630                 case 'xs' :
33631                 case 'sm' :
33632                 case 'tall' :
33633                     
33634                     box.push(item);
33635                     
33636                     break;
33637                 default :
33638                     break;
33639                     
33640             }
33641             
33642         }, this);
33643         
33644         if(box.length){
33645             boxes.push(box);
33646             box = [];
33647         }
33648         
33649         var filterPattern = function(box, length)
33650         {
33651             if(!box.length){
33652                 return;
33653             }
33654             
33655             var match = false;
33656             
33657             var pattern = box.slice(0, length);
33658             
33659             var format = [];
33660             
33661             Roo.each(pattern, function(i){
33662                 format.push(i.size);
33663             }, this);
33664             
33665             Roo.each(standard, function(s){
33666                 
33667                 if(String(s) != String(format)){
33668                     return;
33669                 }
33670                 
33671                 match = true;
33672                 return false;
33673                 
33674             }, this);
33675             
33676             if(!match && length == 1){
33677                 return;
33678             }
33679             
33680             if(!match){
33681                 filterPattern(box, length - 1);
33682                 return;
33683             }
33684                 
33685             queue.push(pattern);
33686
33687             box = box.slice(length, box.length);
33688
33689             filterPattern(box, 4);
33690
33691             return;
33692             
33693         }
33694         
33695         Roo.each(boxes, function(box, k){
33696             
33697             if(!box.length){
33698                 return;
33699             }
33700             
33701             if(box.length == 1){
33702                 queue.push(box);
33703                 return;
33704             }
33705             
33706             filterPattern(box, 4);
33707             
33708         }, this);
33709         
33710         this._processVerticalLayoutQueue( queue, isInstant );
33711         
33712     },
33713     
33714 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33715 //    {
33716 //        if ( !items || !items.length ) {
33717 //            return;
33718 //        }
33719 //
33720 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33721 //        
33722 //    },
33723     
33724     _horizontalLayoutItems : function ( items , isInstant)
33725     {
33726         if ( !items || !items.length || items.length < 3) {
33727             return;
33728         }
33729         
33730         items.reverse();
33731         
33732         var eItems = items.slice(0, 3);
33733         
33734         items = items.slice(3, items.length);
33735         
33736         var standard = [
33737             ['xs', 'xs', 'xs', 'wide'],
33738             ['xs', 'xs', 'wide'],
33739             ['xs', 'xs', 'sm'],
33740             ['xs', 'xs', 'xs'],
33741             ['xs', 'wide'],
33742             ['xs', 'sm'],
33743             ['xs', 'xs'],
33744             ['xs'],
33745             
33746             ['sm', 'xs', 'xs'],
33747             ['sm', 'xs'],
33748             ['sm'],
33749             
33750             ['wide', 'xs', 'xs', 'xs'],
33751             ['wide', 'xs', 'xs'],
33752             ['wide', 'xs'],
33753             ['wide'],
33754             
33755             ['wide-thin']
33756         ];
33757         
33758         var queue = [];
33759         
33760         var boxes = [];
33761         
33762         var box = [];
33763         
33764         Roo.each(items, function(item, k){
33765             
33766             switch (item.size) {
33767                 case 'md' :
33768                 case 'md-left' :
33769                 case 'md-right' :
33770                 case 'tall' :
33771                     
33772                     if(box.length){
33773                         boxes.push(box);
33774                         box = [];
33775                     }
33776                     
33777                     boxes.push([item]);
33778                     
33779                     break;
33780                     
33781                 case 'xs' :
33782                 case 'sm' :
33783                 case 'wide' :
33784                 case 'wide-thin' :
33785                     
33786                     box.push(item);
33787                     
33788                     break;
33789                 default :
33790                     break;
33791                     
33792             }
33793             
33794         }, this);
33795         
33796         if(box.length){
33797             boxes.push(box);
33798             box = [];
33799         }
33800         
33801         var filterPattern = function(box, length)
33802         {
33803             if(!box.length){
33804                 return;
33805             }
33806             
33807             var match = false;
33808             
33809             var pattern = box.slice(0, length);
33810             
33811             var format = [];
33812             
33813             Roo.each(pattern, function(i){
33814                 format.push(i.size);
33815             }, this);
33816             
33817             Roo.each(standard, function(s){
33818                 
33819                 if(String(s) != String(format)){
33820                     return;
33821                 }
33822                 
33823                 match = true;
33824                 return false;
33825                 
33826             }, this);
33827             
33828             if(!match && length == 1){
33829                 return;
33830             }
33831             
33832             if(!match){
33833                 filterPattern(box, length - 1);
33834                 return;
33835             }
33836                 
33837             queue.push(pattern);
33838
33839             box = box.slice(length, box.length);
33840
33841             filterPattern(box, 4);
33842
33843             return;
33844             
33845         }
33846         
33847         Roo.each(boxes, function(box, k){
33848             
33849             if(!box.length){
33850                 return;
33851             }
33852             
33853             if(box.length == 1){
33854                 queue.push(box);
33855                 return;
33856             }
33857             
33858             filterPattern(box, 4);
33859             
33860         }, this);
33861         
33862         
33863         var prune = [];
33864         
33865         var pos = this.el.getBox(true);
33866         
33867         var minX = pos.x;
33868         
33869         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33870         
33871         var hit_end = false;
33872         
33873         Roo.each(queue, function(box){
33874             
33875             if(hit_end){
33876                 
33877                 Roo.each(box, function(b){
33878                 
33879                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33880                     b.el.hide();
33881
33882                 }, this);
33883
33884                 return;
33885             }
33886             
33887             var mx = 0;
33888             
33889             Roo.each(box, function(b){
33890                 
33891                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33892                 b.el.show();
33893
33894                 mx = Math.max(mx, b.x);
33895                 
33896             }, this);
33897             
33898             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33899             
33900             if(maxX < minX){
33901                 
33902                 Roo.each(box, function(b){
33903                 
33904                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33905                     b.el.hide();
33906                     
33907                 }, this);
33908                 
33909                 hit_end = true;
33910                 
33911                 return;
33912             }
33913             
33914             prune.push(box);
33915             
33916         }, this);
33917         
33918         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33919     },
33920     
33921     /** Sets position of item in DOM
33922     * @param {Element} item
33923     * @param {Number} x - horizontal position
33924     * @param {Number} y - vertical position
33925     * @param {Boolean} isInstant - disables transitions
33926     */
33927     _processVerticalLayoutQueue : function( queue, isInstant )
33928     {
33929         var pos = this.el.getBox(true);
33930         var x = pos.x;
33931         var y = pos.y;
33932         var maxY = [];
33933         
33934         for (var i = 0; i < this.cols; i++){
33935             maxY[i] = pos.y;
33936         }
33937         
33938         Roo.each(queue, function(box, k){
33939             
33940             var col = k % this.cols;
33941             
33942             Roo.each(box, function(b,kk){
33943                 
33944                 b.el.position('absolute');
33945                 
33946                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33947                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33948                 
33949                 if(b.size == 'md-left' || b.size == 'md-right'){
33950                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33951                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33952                 }
33953                 
33954                 b.el.setWidth(width);
33955                 b.el.setHeight(height);
33956                 // iframe?
33957                 b.el.select('iframe',true).setSize(width,height);
33958                 
33959             }, this);
33960             
33961             for (var i = 0; i < this.cols; i++){
33962                 
33963                 if(maxY[i] < maxY[col]){
33964                     col = i;
33965                     continue;
33966                 }
33967                 
33968                 col = Math.min(col, i);
33969                 
33970             }
33971             
33972             x = pos.x + col * (this.colWidth + this.padWidth);
33973             
33974             y = maxY[col];
33975             
33976             var positions = [];
33977             
33978             switch (box.length){
33979                 case 1 :
33980                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33981                     break;
33982                 case 2 :
33983                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33984                     break;
33985                 case 3 :
33986                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33987                     break;
33988                 case 4 :
33989                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33990                     break;
33991                 default :
33992                     break;
33993             }
33994             
33995             Roo.each(box, function(b,kk){
33996                 
33997                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33998                 
33999                 var sz = b.el.getSize();
34000                 
34001                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34002                 
34003             }, this);
34004             
34005         }, this);
34006         
34007         var mY = 0;
34008         
34009         for (var i = 0; i < this.cols; i++){
34010             mY = Math.max(mY, maxY[i]);
34011         }
34012         
34013         this.el.setHeight(mY - pos.y);
34014         
34015     },
34016     
34017 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34018 //    {
34019 //        var pos = this.el.getBox(true);
34020 //        var x = pos.x;
34021 //        var y = pos.y;
34022 //        var maxX = pos.right;
34023 //        
34024 //        var maxHeight = 0;
34025 //        
34026 //        Roo.each(items, function(item, k){
34027 //            
34028 //            var c = k % 2;
34029 //            
34030 //            item.el.position('absolute');
34031 //                
34032 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34033 //
34034 //            item.el.setWidth(width);
34035 //
34036 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34037 //
34038 //            item.el.setHeight(height);
34039 //            
34040 //            if(c == 0){
34041 //                item.el.setXY([x, y], isInstant ? false : true);
34042 //            } else {
34043 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34044 //            }
34045 //            
34046 //            y = y + height + this.alternativePadWidth;
34047 //            
34048 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34049 //            
34050 //        }, this);
34051 //        
34052 //        this.el.setHeight(maxHeight);
34053 //        
34054 //    },
34055     
34056     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34057     {
34058         var pos = this.el.getBox(true);
34059         
34060         var minX = pos.x;
34061         var minY = pos.y;
34062         
34063         var maxX = pos.right;
34064         
34065         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34066         
34067         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34068         
34069         Roo.each(queue, function(box, k){
34070             
34071             Roo.each(box, function(b, kk){
34072                 
34073                 b.el.position('absolute');
34074                 
34075                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34076                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34077                 
34078                 if(b.size == 'md-left' || b.size == 'md-right'){
34079                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34080                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34081                 }
34082                 
34083                 b.el.setWidth(width);
34084                 b.el.setHeight(height);
34085                 
34086             }, this);
34087             
34088             if(!box.length){
34089                 return;
34090             }
34091             
34092             var positions = [];
34093             
34094             switch (box.length){
34095                 case 1 :
34096                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34097                     break;
34098                 case 2 :
34099                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34100                     break;
34101                 case 3 :
34102                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34103                     break;
34104                 case 4 :
34105                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34106                     break;
34107                 default :
34108                     break;
34109             }
34110             
34111             Roo.each(box, function(b,kk){
34112                 
34113                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34114                 
34115                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34116                 
34117             }, this);
34118             
34119         }, this);
34120         
34121     },
34122     
34123     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34124     {
34125         Roo.each(eItems, function(b,k){
34126             
34127             b.size = (k == 0) ? 'sm' : 'xs';
34128             b.x = (k == 0) ? 2 : 1;
34129             b.y = (k == 0) ? 2 : 1;
34130             
34131             b.el.position('absolute');
34132             
34133             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34134                 
34135             b.el.setWidth(width);
34136             
34137             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34138             
34139             b.el.setHeight(height);
34140             
34141         }, this);
34142
34143         var positions = [];
34144         
34145         positions.push({
34146             x : maxX - this.unitWidth * 2 - this.gutter,
34147             y : minY
34148         });
34149         
34150         positions.push({
34151             x : maxX - this.unitWidth,
34152             y : minY + (this.unitWidth + this.gutter) * 2
34153         });
34154         
34155         positions.push({
34156             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34157             y : minY
34158         });
34159         
34160         Roo.each(eItems, function(b,k){
34161             
34162             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34163
34164         }, this);
34165         
34166     },
34167     
34168     getVerticalOneBoxColPositions : function(x, y, box)
34169     {
34170         var pos = [];
34171         
34172         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34173         
34174         if(box[0].size == 'md-left'){
34175             rand = 0;
34176         }
34177         
34178         if(box[0].size == 'md-right'){
34179             rand = 1;
34180         }
34181         
34182         pos.push({
34183             x : x + (this.unitWidth + this.gutter) * rand,
34184             y : y
34185         });
34186         
34187         return pos;
34188     },
34189     
34190     getVerticalTwoBoxColPositions : function(x, y, box)
34191     {
34192         var pos = [];
34193         
34194         if(box[0].size == 'xs'){
34195             
34196             pos.push({
34197                 x : x,
34198                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34199             });
34200
34201             pos.push({
34202                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34203                 y : y
34204             });
34205             
34206             return pos;
34207             
34208         }
34209         
34210         pos.push({
34211             x : x,
34212             y : y
34213         });
34214
34215         pos.push({
34216             x : x + (this.unitWidth + this.gutter) * 2,
34217             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34218         });
34219         
34220         return pos;
34221         
34222     },
34223     
34224     getVerticalThreeBoxColPositions : function(x, y, box)
34225     {
34226         var pos = [];
34227         
34228         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34229             
34230             pos.push({
34231                 x : x,
34232                 y : y
34233             });
34234
34235             pos.push({
34236                 x : x + (this.unitWidth + this.gutter) * 1,
34237                 y : y
34238             });
34239             
34240             pos.push({
34241                 x : x + (this.unitWidth + this.gutter) * 2,
34242                 y : y
34243             });
34244             
34245             return pos;
34246             
34247         }
34248         
34249         if(box[0].size == 'xs' && box[1].size == 'xs'){
34250             
34251             pos.push({
34252                 x : x,
34253                 y : y
34254             });
34255
34256             pos.push({
34257                 x : x,
34258                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34259             });
34260             
34261             pos.push({
34262                 x : x + (this.unitWidth + this.gutter) * 1,
34263                 y : y
34264             });
34265             
34266             return pos;
34267             
34268         }
34269         
34270         pos.push({
34271             x : x,
34272             y : y
34273         });
34274
34275         pos.push({
34276             x : x + (this.unitWidth + this.gutter) * 2,
34277             y : y
34278         });
34279
34280         pos.push({
34281             x : x + (this.unitWidth + this.gutter) * 2,
34282             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34283         });
34284             
34285         return pos;
34286         
34287     },
34288     
34289     getVerticalFourBoxColPositions : function(x, y, box)
34290     {
34291         var pos = [];
34292         
34293         if(box[0].size == 'xs'){
34294             
34295             pos.push({
34296                 x : x,
34297                 y : y
34298             });
34299
34300             pos.push({
34301                 x : x,
34302                 y : y + (this.unitHeight + this.gutter) * 1
34303             });
34304             
34305             pos.push({
34306                 x : x,
34307                 y : y + (this.unitHeight + this.gutter) * 2
34308             });
34309             
34310             pos.push({
34311                 x : x + (this.unitWidth + this.gutter) * 1,
34312                 y : y
34313             });
34314             
34315             return pos;
34316             
34317         }
34318         
34319         pos.push({
34320             x : x,
34321             y : y
34322         });
34323
34324         pos.push({
34325             x : x + (this.unitWidth + this.gutter) * 2,
34326             y : y
34327         });
34328
34329         pos.push({
34330             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34331             y : y + (this.unitHeight + this.gutter) * 1
34332         });
34333
34334         pos.push({
34335             x : x + (this.unitWidth + this.gutter) * 2,
34336             y : y + (this.unitWidth + this.gutter) * 2
34337         });
34338
34339         return pos;
34340         
34341     },
34342     
34343     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34344     {
34345         var pos = [];
34346         
34347         if(box[0].size == 'md-left'){
34348             pos.push({
34349                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34350                 y : minY
34351             });
34352             
34353             return pos;
34354         }
34355         
34356         if(box[0].size == 'md-right'){
34357             pos.push({
34358                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34359                 y : minY + (this.unitWidth + this.gutter) * 1
34360             });
34361             
34362             return pos;
34363         }
34364         
34365         var rand = Math.floor(Math.random() * (4 - box[0].y));
34366         
34367         pos.push({
34368             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34369             y : minY + (this.unitWidth + this.gutter) * rand
34370         });
34371         
34372         return pos;
34373         
34374     },
34375     
34376     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34377     {
34378         var pos = [];
34379         
34380         if(box[0].size == 'xs'){
34381             
34382             pos.push({
34383                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34384                 y : minY
34385             });
34386
34387             pos.push({
34388                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34389                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34390             });
34391             
34392             return pos;
34393             
34394         }
34395         
34396         pos.push({
34397             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34398             y : minY
34399         });
34400
34401         pos.push({
34402             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34403             y : minY + (this.unitWidth + this.gutter) * 2
34404         });
34405         
34406         return pos;
34407         
34408     },
34409     
34410     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34411     {
34412         var pos = [];
34413         
34414         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34415             
34416             pos.push({
34417                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34418                 y : minY
34419             });
34420
34421             pos.push({
34422                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34423                 y : minY + (this.unitWidth + this.gutter) * 1
34424             });
34425             
34426             pos.push({
34427                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34428                 y : minY + (this.unitWidth + this.gutter) * 2
34429             });
34430             
34431             return pos;
34432             
34433         }
34434         
34435         if(box[0].size == 'xs' && box[1].size == 'xs'){
34436             
34437             pos.push({
34438                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34439                 y : minY
34440             });
34441
34442             pos.push({
34443                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34444                 y : minY
34445             });
34446             
34447             pos.push({
34448                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34449                 y : minY + (this.unitWidth + this.gutter) * 1
34450             });
34451             
34452             return pos;
34453             
34454         }
34455         
34456         pos.push({
34457             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34458             y : minY
34459         });
34460
34461         pos.push({
34462             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34463             y : minY + (this.unitWidth + this.gutter) * 2
34464         });
34465
34466         pos.push({
34467             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34468             y : minY + (this.unitWidth + this.gutter) * 2
34469         });
34470             
34471         return pos;
34472         
34473     },
34474     
34475     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34476     {
34477         var pos = [];
34478         
34479         if(box[0].size == 'xs'){
34480             
34481             pos.push({
34482                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34483                 y : minY
34484             });
34485
34486             pos.push({
34487                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34488                 y : minY
34489             });
34490             
34491             pos.push({
34492                 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),
34493                 y : minY
34494             });
34495             
34496             pos.push({
34497                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34498                 y : minY + (this.unitWidth + this.gutter) * 1
34499             });
34500             
34501             return pos;
34502             
34503         }
34504         
34505         pos.push({
34506             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34507             y : minY
34508         });
34509         
34510         pos.push({
34511             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34512             y : minY + (this.unitWidth + this.gutter) * 2
34513         });
34514         
34515         pos.push({
34516             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34517             y : minY + (this.unitWidth + this.gutter) * 2
34518         });
34519         
34520         pos.push({
34521             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),
34522             y : minY + (this.unitWidth + this.gutter) * 2
34523         });
34524
34525         return pos;
34526         
34527     },
34528     
34529     /**
34530     * remove a Masonry Brick
34531     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34532     */
34533     removeBrick : function(brick_id)
34534     {
34535         if (!brick_id) {
34536             return;
34537         }
34538         
34539         for (var i = 0; i<this.bricks.length; i++) {
34540             if (this.bricks[i].id == brick_id) {
34541                 this.bricks.splice(i,1);
34542                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34543                 this.initial();
34544             }
34545         }
34546     },
34547     
34548     /**
34549     * adds a Masonry Brick
34550     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34551     */
34552     addBrick : function(cfg)
34553     {
34554         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34555         //this.register(cn);
34556         cn.parentId = this.id;
34557         cn.render(this.el);
34558         return cn;
34559     },
34560     
34561     /**
34562     * register a Masonry Brick
34563     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34564     */
34565     
34566     register : function(brick)
34567     {
34568         this.bricks.push(brick);
34569         brick.masonryId = this.id;
34570     },
34571     
34572     /**
34573     * clear all the Masonry Brick
34574     */
34575     clearAll : function()
34576     {
34577         this.bricks = [];
34578         //this.getChildContainer().dom.innerHTML = "";
34579         this.el.dom.innerHTML = '';
34580     },
34581     
34582     getSelected : function()
34583     {
34584         if (!this.selectedBrick) {
34585             return false;
34586         }
34587         
34588         return this.selectedBrick;
34589     }
34590 });
34591
34592 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34593     
34594     groups: {},
34595      /**
34596     * register a Masonry Layout
34597     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34598     */
34599     
34600     register : function(layout)
34601     {
34602         this.groups[layout.id] = layout;
34603     },
34604     /**
34605     * fetch a  Masonry Layout based on the masonry layout ID
34606     * @param {string} the masonry layout to add
34607     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34608     */
34609     
34610     get: function(layout_id) {
34611         if (typeof(this.groups[layout_id]) == 'undefined') {
34612             return false;
34613         }
34614         return this.groups[layout_id] ;
34615     }
34616     
34617     
34618     
34619 });
34620
34621  
34622
34623  /**
34624  *
34625  * This is based on 
34626  * http://masonry.desandro.com
34627  *
34628  * The idea is to render all the bricks based on vertical width...
34629  *
34630  * The original code extends 'outlayer' - we might need to use that....
34631  * 
34632  */
34633
34634
34635 /**
34636  * @class Roo.bootstrap.LayoutMasonryAuto
34637  * @extends Roo.bootstrap.Component
34638  * Bootstrap Layout Masonry class
34639  * 
34640  * @constructor
34641  * Create a new Element
34642  * @param {Object} config The config object
34643  */
34644
34645 Roo.bootstrap.LayoutMasonryAuto = function(config){
34646     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34647 };
34648
34649 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34650     
34651       /**
34652      * @cfg {Boolean} isFitWidth  - resize the width..
34653      */   
34654     isFitWidth : false,  // options..
34655     /**
34656      * @cfg {Boolean} isOriginLeft = left align?
34657      */   
34658     isOriginLeft : true,
34659     /**
34660      * @cfg {Boolean} isOriginTop = top align?
34661      */   
34662     isOriginTop : false,
34663     /**
34664      * @cfg {Boolean} isLayoutInstant = no animation?
34665      */   
34666     isLayoutInstant : false, // needed?
34667     /**
34668      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34669      */   
34670     isResizingContainer : true,
34671     /**
34672      * @cfg {Number} columnWidth  width of the columns 
34673      */   
34674     
34675     columnWidth : 0,
34676     
34677     /**
34678      * @cfg {Number} maxCols maximum number of columns
34679      */   
34680     
34681     maxCols: 0,
34682     /**
34683      * @cfg {Number} padHeight padding below box..
34684      */   
34685     
34686     padHeight : 10, 
34687     
34688     /**
34689      * @cfg {Boolean} isAutoInitial defalut true
34690      */   
34691     
34692     isAutoInitial : true, 
34693     
34694     // private?
34695     gutter : 0,
34696     
34697     containerWidth: 0,
34698     initialColumnWidth : 0,
34699     currentSize : null,
34700     
34701     colYs : null, // array.
34702     maxY : 0,
34703     padWidth: 10,
34704     
34705     
34706     tag: 'div',
34707     cls: '',
34708     bricks: null, //CompositeElement
34709     cols : 0, // array?
34710     // element : null, // wrapped now this.el
34711     _isLayoutInited : null, 
34712     
34713     
34714     getAutoCreate : function(){
34715         
34716         var cfg = {
34717             tag: this.tag,
34718             cls: 'blog-masonary-wrapper ' + this.cls,
34719             cn : {
34720                 cls : 'mas-boxes masonary'
34721             }
34722         };
34723         
34724         return cfg;
34725     },
34726     
34727     getChildContainer: function( )
34728     {
34729         if (this.boxesEl) {
34730             return this.boxesEl;
34731         }
34732         
34733         this.boxesEl = this.el.select('.mas-boxes').first();
34734         
34735         return this.boxesEl;
34736     },
34737     
34738     
34739     initEvents : function()
34740     {
34741         var _this = this;
34742         
34743         if(this.isAutoInitial){
34744             Roo.log('hook children rendered');
34745             this.on('childrenrendered', function() {
34746                 Roo.log('children rendered');
34747                 _this.initial();
34748             } ,this);
34749         }
34750         
34751     },
34752     
34753     initial : function()
34754     {
34755         this.reloadItems();
34756
34757         this.currentSize = this.el.getBox(true);
34758
34759         /// was window resize... - let's see if this works..
34760         Roo.EventManager.onWindowResize(this.resize, this); 
34761
34762         if(!this.isAutoInitial){
34763             this.layout();
34764             return;
34765         }
34766         
34767         this.layout.defer(500,this);
34768     },
34769     
34770     reloadItems: function()
34771     {
34772         this.bricks = this.el.select('.masonry-brick', true);
34773         
34774         this.bricks.each(function(b) {
34775             //Roo.log(b.getSize());
34776             if (!b.attr('originalwidth')) {
34777                 b.attr('originalwidth',  b.getSize().width);
34778             }
34779             
34780         });
34781         
34782         Roo.log(this.bricks.elements.length);
34783     },
34784     
34785     resize : function()
34786     {
34787         Roo.log('resize');
34788         var cs = this.el.getBox(true);
34789         
34790         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34791             Roo.log("no change in with or X");
34792             return;
34793         }
34794         this.currentSize = cs;
34795         this.layout();
34796     },
34797     
34798     layout : function()
34799     {
34800          Roo.log('layout');
34801         this._resetLayout();
34802         //this._manageStamps();
34803       
34804         // don't animate first layout
34805         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34806         this.layoutItems( isInstant );
34807       
34808         // flag for initalized
34809         this._isLayoutInited = true;
34810     },
34811     
34812     layoutItems : function( isInstant )
34813     {
34814         //var items = this._getItemsForLayout( this.items );
34815         // original code supports filtering layout items.. we just ignore it..
34816         
34817         this._layoutItems( this.bricks , isInstant );
34818       
34819         this._postLayout();
34820     },
34821     _layoutItems : function ( items , isInstant)
34822     {
34823        //this.fireEvent( 'layout', this, items );
34824     
34825
34826         if ( !items || !items.elements.length ) {
34827           // no items, emit event with empty array
34828             return;
34829         }
34830
34831         var queue = [];
34832         items.each(function(item) {
34833             Roo.log("layout item");
34834             Roo.log(item);
34835             // get x/y object from method
34836             var position = this._getItemLayoutPosition( item );
34837             // enqueue
34838             position.item = item;
34839             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34840             queue.push( position );
34841         }, this);
34842       
34843         this._processLayoutQueue( queue );
34844     },
34845     /** Sets position of item in DOM
34846     * @param {Element} item
34847     * @param {Number} x - horizontal position
34848     * @param {Number} y - vertical position
34849     * @param {Boolean} isInstant - disables transitions
34850     */
34851     _processLayoutQueue : function( queue )
34852     {
34853         for ( var i=0, len = queue.length; i < len; i++ ) {
34854             var obj = queue[i];
34855             obj.item.position('absolute');
34856             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34857         }
34858     },
34859       
34860     
34861     /**
34862     * Any logic you want to do after each layout,
34863     * i.e. size the container
34864     */
34865     _postLayout : function()
34866     {
34867         this.resizeContainer();
34868     },
34869     
34870     resizeContainer : function()
34871     {
34872         if ( !this.isResizingContainer ) {
34873             return;
34874         }
34875         var size = this._getContainerSize();
34876         if ( size ) {
34877             this.el.setSize(size.width,size.height);
34878             this.boxesEl.setSize(size.width,size.height);
34879         }
34880     },
34881     
34882     
34883     
34884     _resetLayout : function()
34885     {
34886         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34887         this.colWidth = this.el.getWidth();
34888         //this.gutter = this.el.getWidth(); 
34889         
34890         this.measureColumns();
34891
34892         // reset column Y
34893         var i = this.cols;
34894         this.colYs = [];
34895         while (i--) {
34896             this.colYs.push( 0 );
34897         }
34898     
34899         this.maxY = 0;
34900     },
34901
34902     measureColumns : function()
34903     {
34904         this.getContainerWidth();
34905       // if columnWidth is 0, default to outerWidth of first item
34906         if ( !this.columnWidth ) {
34907             var firstItem = this.bricks.first();
34908             Roo.log(firstItem);
34909             this.columnWidth  = this.containerWidth;
34910             if (firstItem && firstItem.attr('originalwidth') ) {
34911                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34912             }
34913             // columnWidth fall back to item of first element
34914             Roo.log("set column width?");
34915                         this.initialColumnWidth = this.columnWidth  ;
34916
34917             // if first elem has no width, default to size of container
34918             
34919         }
34920         
34921         
34922         if (this.initialColumnWidth) {
34923             this.columnWidth = this.initialColumnWidth;
34924         }
34925         
34926         
34927             
34928         // column width is fixed at the top - however if container width get's smaller we should
34929         // reduce it...
34930         
34931         // this bit calcs how man columns..
34932             
34933         var columnWidth = this.columnWidth += this.gutter;
34934       
34935         // calculate columns
34936         var containerWidth = this.containerWidth + this.gutter;
34937         
34938         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34939         // fix rounding errors, typically with gutters
34940         var excess = columnWidth - containerWidth % columnWidth;
34941         
34942         
34943         // if overshoot is less than a pixel, round up, otherwise floor it
34944         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34945         cols = Math[ mathMethod ]( cols );
34946         this.cols = Math.max( cols, 1 );
34947         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34948         
34949          // padding positioning..
34950         var totalColWidth = this.cols * this.columnWidth;
34951         var padavail = this.containerWidth - totalColWidth;
34952         // so for 2 columns - we need 3 'pads'
34953         
34954         var padNeeded = (1+this.cols) * this.padWidth;
34955         
34956         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34957         
34958         this.columnWidth += padExtra
34959         //this.padWidth = Math.floor(padavail /  ( this.cols));
34960         
34961         // adjust colum width so that padding is fixed??
34962         
34963         // we have 3 columns ... total = width * 3
34964         // we have X left over... that should be used by 
34965         
34966         //if (this.expandC) {
34967             
34968         //}
34969         
34970         
34971         
34972     },
34973     
34974     getContainerWidth : function()
34975     {
34976        /* // container is parent if fit width
34977         var container = this.isFitWidth ? this.element.parentNode : this.element;
34978         // check that this.size and size are there
34979         // IE8 triggers resize on body size change, so they might not be
34980         
34981         var size = getSize( container );  //FIXME
34982         this.containerWidth = size && size.innerWidth; //FIXME
34983         */
34984          
34985         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34986         
34987     },
34988     
34989     _getItemLayoutPosition : function( item )  // what is item?
34990     {
34991         // we resize the item to our columnWidth..
34992       
34993         item.setWidth(this.columnWidth);
34994         item.autoBoxAdjust  = false;
34995         
34996         var sz = item.getSize();
34997  
34998         // how many columns does this brick span
34999         var remainder = this.containerWidth % this.columnWidth;
35000         
35001         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35002         // round if off by 1 pixel, otherwise use ceil
35003         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35004         colSpan = Math.min( colSpan, this.cols );
35005         
35006         // normally this should be '1' as we dont' currently allow multi width columns..
35007         
35008         var colGroup = this._getColGroup( colSpan );
35009         // get the minimum Y value from the columns
35010         var minimumY = Math.min.apply( Math, colGroup );
35011         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35012         
35013         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35014          
35015         // position the brick
35016         var position = {
35017             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35018             y: this.currentSize.y + minimumY + this.padHeight
35019         };
35020         
35021         Roo.log(position);
35022         // apply setHeight to necessary columns
35023         var setHeight = minimumY + sz.height + this.padHeight;
35024         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35025         
35026         var setSpan = this.cols + 1 - colGroup.length;
35027         for ( var i = 0; i < setSpan; i++ ) {
35028           this.colYs[ shortColIndex + i ] = setHeight ;
35029         }
35030       
35031         return position;
35032     },
35033     
35034     /**
35035      * @param {Number} colSpan - number of columns the element spans
35036      * @returns {Array} colGroup
35037      */
35038     _getColGroup : function( colSpan )
35039     {
35040         if ( colSpan < 2 ) {
35041           // if brick spans only one column, use all the column Ys
35042           return this.colYs;
35043         }
35044       
35045         var colGroup = [];
35046         // how many different places could this brick fit horizontally
35047         var groupCount = this.cols + 1 - colSpan;
35048         // for each group potential horizontal position
35049         for ( var i = 0; i < groupCount; i++ ) {
35050           // make an array of colY values for that one group
35051           var groupColYs = this.colYs.slice( i, i + colSpan );
35052           // and get the max value of the array
35053           colGroup[i] = Math.max.apply( Math, groupColYs );
35054         }
35055         return colGroup;
35056     },
35057     /*
35058     _manageStamp : function( stamp )
35059     {
35060         var stampSize =  stamp.getSize();
35061         var offset = stamp.getBox();
35062         // get the columns that this stamp affects
35063         var firstX = this.isOriginLeft ? offset.x : offset.right;
35064         var lastX = firstX + stampSize.width;
35065         var firstCol = Math.floor( firstX / this.columnWidth );
35066         firstCol = Math.max( 0, firstCol );
35067         
35068         var lastCol = Math.floor( lastX / this.columnWidth );
35069         // lastCol should not go over if multiple of columnWidth #425
35070         lastCol -= lastX % this.columnWidth ? 0 : 1;
35071         lastCol = Math.min( this.cols - 1, lastCol );
35072         
35073         // set colYs to bottom of the stamp
35074         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35075             stampSize.height;
35076             
35077         for ( var i = firstCol; i <= lastCol; i++ ) {
35078           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35079         }
35080     },
35081     */
35082     
35083     _getContainerSize : function()
35084     {
35085         this.maxY = Math.max.apply( Math, this.colYs );
35086         var size = {
35087             height: this.maxY
35088         };
35089       
35090         if ( this.isFitWidth ) {
35091             size.width = this._getContainerFitWidth();
35092         }
35093       
35094         return size;
35095     },
35096     
35097     _getContainerFitWidth : function()
35098     {
35099         var unusedCols = 0;
35100         // count unused columns
35101         var i = this.cols;
35102         while ( --i ) {
35103           if ( this.colYs[i] !== 0 ) {
35104             break;
35105           }
35106           unusedCols++;
35107         }
35108         // fit container to columns that have been used
35109         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35110     },
35111     
35112     needsResizeLayout : function()
35113     {
35114         var previousWidth = this.containerWidth;
35115         this.getContainerWidth();
35116         return previousWidth !== this.containerWidth;
35117     }
35118  
35119 });
35120
35121  
35122
35123  /*
35124  * - LGPL
35125  *
35126  * element
35127  * 
35128  */
35129
35130 /**
35131  * @class Roo.bootstrap.MasonryBrick
35132  * @extends Roo.bootstrap.Component
35133  * Bootstrap MasonryBrick class
35134  * 
35135  * @constructor
35136  * Create a new MasonryBrick
35137  * @param {Object} config The config object
35138  */
35139
35140 Roo.bootstrap.MasonryBrick = function(config){
35141     
35142     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35143     
35144     Roo.bootstrap.MasonryBrick.register(this);
35145     
35146     this.addEvents({
35147         // raw events
35148         /**
35149          * @event click
35150          * When a MasonryBrick is clcik
35151          * @param {Roo.bootstrap.MasonryBrick} this
35152          * @param {Roo.EventObject} e
35153          */
35154         "click" : true
35155     });
35156 };
35157
35158 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35159     
35160     /**
35161      * @cfg {String} title
35162      */   
35163     title : '',
35164     /**
35165      * @cfg {String} html
35166      */   
35167     html : '',
35168     /**
35169      * @cfg {String} bgimage
35170      */   
35171     bgimage : '',
35172     /**
35173      * @cfg {String} videourl
35174      */   
35175     videourl : '',
35176     /**
35177      * @cfg {String} cls
35178      */   
35179     cls : '',
35180     /**
35181      * @cfg {String} href
35182      */   
35183     href : '',
35184     /**
35185      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35186      */   
35187     size : 'xs',
35188     
35189     /**
35190      * @cfg {String} placetitle (center|bottom)
35191      */   
35192     placetitle : '',
35193     
35194     /**
35195      * @cfg {Boolean} isFitContainer defalut true
35196      */   
35197     isFitContainer : true, 
35198     
35199     /**
35200      * @cfg {Boolean} preventDefault defalut false
35201      */   
35202     preventDefault : false, 
35203     
35204     /**
35205      * @cfg {Boolean} inverse defalut false
35206      */   
35207     maskInverse : false, 
35208     
35209     getAutoCreate : function()
35210     {
35211         if(!this.isFitContainer){
35212             return this.getSplitAutoCreate();
35213         }
35214         
35215         var cls = 'masonry-brick masonry-brick-full';
35216         
35217         if(this.href.length){
35218             cls += ' masonry-brick-link';
35219         }
35220         
35221         if(this.bgimage.length){
35222             cls += ' masonry-brick-image';
35223         }
35224         
35225         if(this.maskInverse){
35226             cls += ' mask-inverse';
35227         }
35228         
35229         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35230             cls += ' enable-mask';
35231         }
35232         
35233         if(this.size){
35234             cls += ' masonry-' + this.size + '-brick';
35235         }
35236         
35237         if(this.placetitle.length){
35238             
35239             switch (this.placetitle) {
35240                 case 'center' :
35241                     cls += ' masonry-center-title';
35242                     break;
35243                 case 'bottom' :
35244                     cls += ' masonry-bottom-title';
35245                     break;
35246                 default:
35247                     break;
35248             }
35249             
35250         } else {
35251             if(!this.html.length && !this.bgimage.length){
35252                 cls += ' masonry-center-title';
35253             }
35254
35255             if(!this.html.length && this.bgimage.length){
35256                 cls += ' masonry-bottom-title';
35257             }
35258         }
35259         
35260         if(this.cls){
35261             cls += ' ' + this.cls;
35262         }
35263         
35264         var cfg = {
35265             tag: (this.href.length) ? 'a' : 'div',
35266             cls: cls,
35267             cn: [
35268                 {
35269                     tag: 'div',
35270                     cls: 'masonry-brick-mask'
35271                 },
35272                 {
35273                     tag: 'div',
35274                     cls: 'masonry-brick-paragraph',
35275                     cn: []
35276                 }
35277             ]
35278         };
35279         
35280         if(this.href.length){
35281             cfg.href = this.href;
35282         }
35283         
35284         var cn = cfg.cn[1].cn;
35285         
35286         if(this.title.length){
35287             cn.push({
35288                 tag: 'h4',
35289                 cls: 'masonry-brick-title',
35290                 html: this.title
35291             });
35292         }
35293         
35294         if(this.html.length){
35295             cn.push({
35296                 tag: 'p',
35297                 cls: 'masonry-brick-text',
35298                 html: this.html
35299             });
35300         }
35301         
35302         if (!this.title.length && !this.html.length) {
35303             cfg.cn[1].cls += ' hide';
35304         }
35305         
35306         if(this.bgimage.length){
35307             cfg.cn.push({
35308                 tag: 'img',
35309                 cls: 'masonry-brick-image-view',
35310                 src: this.bgimage
35311             });
35312         }
35313         
35314         if(this.videourl.length){
35315             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35316             // youtube support only?
35317             cfg.cn.push({
35318                 tag: 'iframe',
35319                 cls: 'masonry-brick-image-view',
35320                 src: vurl,
35321                 frameborder : 0,
35322                 allowfullscreen : true
35323             });
35324         }
35325         
35326         return cfg;
35327         
35328     },
35329     
35330     getSplitAutoCreate : function()
35331     {
35332         var cls = 'masonry-brick masonry-brick-split';
35333         
35334         if(this.href.length){
35335             cls += ' masonry-brick-link';
35336         }
35337         
35338         if(this.bgimage.length){
35339             cls += ' masonry-brick-image';
35340         }
35341         
35342         if(this.size){
35343             cls += ' masonry-' + this.size + '-brick';
35344         }
35345         
35346         switch (this.placetitle) {
35347             case 'center' :
35348                 cls += ' masonry-center-title';
35349                 break;
35350             case 'bottom' :
35351                 cls += ' masonry-bottom-title';
35352                 break;
35353             default:
35354                 if(!this.bgimage.length){
35355                     cls += ' masonry-center-title';
35356                 }
35357
35358                 if(this.bgimage.length){
35359                     cls += ' masonry-bottom-title';
35360                 }
35361                 break;
35362         }
35363         
35364         if(this.cls){
35365             cls += ' ' + this.cls;
35366         }
35367         
35368         var cfg = {
35369             tag: (this.href.length) ? 'a' : 'div',
35370             cls: cls,
35371             cn: [
35372                 {
35373                     tag: 'div',
35374                     cls: 'masonry-brick-split-head',
35375                     cn: [
35376                         {
35377                             tag: 'div',
35378                             cls: 'masonry-brick-paragraph',
35379                             cn: []
35380                         }
35381                     ]
35382                 },
35383                 {
35384                     tag: 'div',
35385                     cls: 'masonry-brick-split-body',
35386                     cn: []
35387                 }
35388             ]
35389         };
35390         
35391         if(this.href.length){
35392             cfg.href = this.href;
35393         }
35394         
35395         if(this.title.length){
35396             cfg.cn[0].cn[0].cn.push({
35397                 tag: 'h4',
35398                 cls: 'masonry-brick-title',
35399                 html: this.title
35400             });
35401         }
35402         
35403         if(this.html.length){
35404             cfg.cn[1].cn.push({
35405                 tag: 'p',
35406                 cls: 'masonry-brick-text',
35407                 html: this.html
35408             });
35409         }
35410
35411         if(this.bgimage.length){
35412             cfg.cn[0].cn.push({
35413                 tag: 'img',
35414                 cls: 'masonry-brick-image-view',
35415                 src: this.bgimage
35416             });
35417         }
35418         
35419         if(this.videourl.length){
35420             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35421             // youtube support only?
35422             cfg.cn[0].cn.cn.push({
35423                 tag: 'iframe',
35424                 cls: 'masonry-brick-image-view',
35425                 src: vurl,
35426                 frameborder : 0,
35427                 allowfullscreen : true
35428             });
35429         }
35430         
35431         return cfg;
35432     },
35433     
35434     initEvents: function() 
35435     {
35436         switch (this.size) {
35437             case 'xs' :
35438                 this.x = 1;
35439                 this.y = 1;
35440                 break;
35441             case 'sm' :
35442                 this.x = 2;
35443                 this.y = 2;
35444                 break;
35445             case 'md' :
35446             case 'md-left' :
35447             case 'md-right' :
35448                 this.x = 3;
35449                 this.y = 3;
35450                 break;
35451             case 'tall' :
35452                 this.x = 2;
35453                 this.y = 3;
35454                 break;
35455             case 'wide' :
35456                 this.x = 3;
35457                 this.y = 2;
35458                 break;
35459             case 'wide-thin' :
35460                 this.x = 3;
35461                 this.y = 1;
35462                 break;
35463                         
35464             default :
35465                 break;
35466         }
35467         
35468         if(Roo.isTouch){
35469             this.el.on('touchstart', this.onTouchStart, this);
35470             this.el.on('touchmove', this.onTouchMove, this);
35471             this.el.on('touchend', this.onTouchEnd, this);
35472             this.el.on('contextmenu', this.onContextMenu, this);
35473         } else {
35474             this.el.on('mouseenter'  ,this.enter, this);
35475             this.el.on('mouseleave', this.leave, this);
35476             this.el.on('click', this.onClick, this);
35477         }
35478         
35479         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35480             this.parent().bricks.push(this);   
35481         }
35482         
35483     },
35484     
35485     onClick: function(e, el)
35486     {
35487         var time = this.endTimer - this.startTimer;
35488         // Roo.log(e.preventDefault());
35489         if(Roo.isTouch){
35490             if(time > 1000){
35491                 e.preventDefault();
35492                 return;
35493             }
35494         }
35495         
35496         if(!this.preventDefault){
35497             return;
35498         }
35499         
35500         e.preventDefault();
35501         
35502         if (this.activeClass != '') {
35503             this.selectBrick();
35504         }
35505         
35506         this.fireEvent('click', this, e);
35507     },
35508     
35509     enter: function(e, el)
35510     {
35511         e.preventDefault();
35512         
35513         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35514             return;
35515         }
35516         
35517         if(this.bgimage.length && this.html.length){
35518             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35519         }
35520     },
35521     
35522     leave: function(e, el)
35523     {
35524         e.preventDefault();
35525         
35526         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35527             return;
35528         }
35529         
35530         if(this.bgimage.length && this.html.length){
35531             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35532         }
35533     },
35534     
35535     onTouchStart: function(e, el)
35536     {
35537 //        e.preventDefault();
35538         
35539         this.touchmoved = false;
35540         
35541         if(!this.isFitContainer){
35542             return;
35543         }
35544         
35545         if(!this.bgimage.length || !this.html.length){
35546             return;
35547         }
35548         
35549         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35550         
35551         this.timer = new Date().getTime();
35552         
35553     },
35554     
35555     onTouchMove: function(e, el)
35556     {
35557         this.touchmoved = true;
35558     },
35559     
35560     onContextMenu : function(e,el)
35561     {
35562         e.preventDefault();
35563         e.stopPropagation();
35564         return false;
35565     },
35566     
35567     onTouchEnd: function(e, el)
35568     {
35569 //        e.preventDefault();
35570         
35571         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35572         
35573             this.leave(e,el);
35574             
35575             return;
35576         }
35577         
35578         if(!this.bgimage.length || !this.html.length){
35579             
35580             if(this.href.length){
35581                 window.location.href = this.href;
35582             }
35583             
35584             return;
35585         }
35586         
35587         if(!this.isFitContainer){
35588             return;
35589         }
35590         
35591         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35592         
35593         window.location.href = this.href;
35594     },
35595     
35596     //selection on single brick only
35597     selectBrick : function() {
35598         
35599         if (!this.parentId) {
35600             return;
35601         }
35602         
35603         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35604         var index = m.selectedBrick.indexOf(this.id);
35605         
35606         if ( index > -1) {
35607             m.selectedBrick.splice(index,1);
35608             this.el.removeClass(this.activeClass);
35609             return;
35610         }
35611         
35612         for(var i = 0; i < m.selectedBrick.length; i++) {
35613             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35614             b.el.removeClass(b.activeClass);
35615         }
35616         
35617         m.selectedBrick = [];
35618         
35619         m.selectedBrick.push(this.id);
35620         this.el.addClass(this.activeClass);
35621         return;
35622     },
35623     
35624     isSelected : function(){
35625         return this.el.hasClass(this.activeClass);
35626         
35627     }
35628 });
35629
35630 Roo.apply(Roo.bootstrap.MasonryBrick, {
35631     
35632     //groups: {},
35633     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35634      /**
35635     * register a Masonry Brick
35636     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35637     */
35638     
35639     register : function(brick)
35640     {
35641         //this.groups[brick.id] = brick;
35642         this.groups.add(brick.id, brick);
35643     },
35644     /**
35645     * fetch a  masonry brick based on the masonry brick ID
35646     * @param {string} the masonry brick to add
35647     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35648     */
35649     
35650     get: function(brick_id) 
35651     {
35652         // if (typeof(this.groups[brick_id]) == 'undefined') {
35653         //     return false;
35654         // }
35655         // return this.groups[brick_id] ;
35656         
35657         if(this.groups.key(brick_id)) {
35658             return this.groups.key(brick_id);
35659         }
35660         
35661         return false;
35662     }
35663     
35664     
35665     
35666 });
35667
35668  /*
35669  * - LGPL
35670  *
35671  * element
35672  * 
35673  */
35674
35675 /**
35676  * @class Roo.bootstrap.Brick
35677  * @extends Roo.bootstrap.Component
35678  * Bootstrap Brick class
35679  * 
35680  * @constructor
35681  * Create a new Brick
35682  * @param {Object} config The config object
35683  */
35684
35685 Roo.bootstrap.Brick = function(config){
35686     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35687     
35688     this.addEvents({
35689         // raw events
35690         /**
35691          * @event click
35692          * When a Brick is click
35693          * @param {Roo.bootstrap.Brick} this
35694          * @param {Roo.EventObject} e
35695          */
35696         "click" : true
35697     });
35698 };
35699
35700 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35701     
35702     /**
35703      * @cfg {String} title
35704      */   
35705     title : '',
35706     /**
35707      * @cfg {String} html
35708      */   
35709     html : '',
35710     /**
35711      * @cfg {String} bgimage
35712      */   
35713     bgimage : '',
35714     /**
35715      * @cfg {String} cls
35716      */   
35717     cls : '',
35718     /**
35719      * @cfg {String} href
35720      */   
35721     href : '',
35722     /**
35723      * @cfg {String} video
35724      */   
35725     video : '',
35726     /**
35727      * @cfg {Boolean} square
35728      */   
35729     square : true,
35730     
35731     getAutoCreate : function()
35732     {
35733         var cls = 'roo-brick';
35734         
35735         if(this.href.length){
35736             cls += ' roo-brick-link';
35737         }
35738         
35739         if(this.bgimage.length){
35740             cls += ' roo-brick-image';
35741         }
35742         
35743         if(!this.html.length && !this.bgimage.length){
35744             cls += ' roo-brick-center-title';
35745         }
35746         
35747         if(!this.html.length && this.bgimage.length){
35748             cls += ' roo-brick-bottom-title';
35749         }
35750         
35751         if(this.cls){
35752             cls += ' ' + this.cls;
35753         }
35754         
35755         var cfg = {
35756             tag: (this.href.length) ? 'a' : 'div',
35757             cls: cls,
35758             cn: [
35759                 {
35760                     tag: 'div',
35761                     cls: 'roo-brick-paragraph',
35762                     cn: []
35763                 }
35764             ]
35765         };
35766         
35767         if(this.href.length){
35768             cfg.href = this.href;
35769         }
35770         
35771         var cn = cfg.cn[0].cn;
35772         
35773         if(this.title.length){
35774             cn.push({
35775                 tag: 'h4',
35776                 cls: 'roo-brick-title',
35777                 html: this.title
35778             });
35779         }
35780         
35781         if(this.html.length){
35782             cn.push({
35783                 tag: 'p',
35784                 cls: 'roo-brick-text',
35785                 html: this.html
35786             });
35787         } else {
35788             cn.cls += ' hide';
35789         }
35790         
35791         if(this.bgimage.length){
35792             cfg.cn.push({
35793                 tag: 'img',
35794                 cls: 'roo-brick-image-view',
35795                 src: this.bgimage
35796             });
35797         }
35798         
35799         return cfg;
35800     },
35801     
35802     initEvents: function() 
35803     {
35804         if(this.title.length || this.html.length){
35805             this.el.on('mouseenter'  ,this.enter, this);
35806             this.el.on('mouseleave', this.leave, this);
35807         }
35808         
35809         Roo.EventManager.onWindowResize(this.resize, this); 
35810         
35811         if(this.bgimage.length){
35812             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35813             this.imageEl.on('load', this.onImageLoad, this);
35814             return;
35815         }
35816         
35817         this.resize();
35818     },
35819     
35820     onImageLoad : function()
35821     {
35822         this.resize();
35823     },
35824     
35825     resize : function()
35826     {
35827         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35828         
35829         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35830         
35831         if(this.bgimage.length){
35832             var image = this.el.select('.roo-brick-image-view', true).first();
35833             
35834             image.setWidth(paragraph.getWidth());
35835             
35836             if(this.square){
35837                 image.setHeight(paragraph.getWidth());
35838             }
35839             
35840             this.el.setHeight(image.getHeight());
35841             paragraph.setHeight(image.getHeight());
35842             
35843         }
35844         
35845     },
35846     
35847     enter: function(e, el)
35848     {
35849         e.preventDefault();
35850         
35851         if(this.bgimage.length){
35852             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35853             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35854         }
35855     },
35856     
35857     leave: function(e, el)
35858     {
35859         e.preventDefault();
35860         
35861         if(this.bgimage.length){
35862             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35863             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35864         }
35865     }
35866     
35867 });
35868
35869  
35870
35871  /*
35872  * - LGPL
35873  *
35874  * Number field 
35875  */
35876
35877 /**
35878  * @class Roo.bootstrap.NumberField
35879  * @extends Roo.bootstrap.Input
35880  * Bootstrap NumberField class
35881  * 
35882  * 
35883  * 
35884  * 
35885  * @constructor
35886  * Create a new NumberField
35887  * @param {Object} config The config object
35888  */
35889
35890 Roo.bootstrap.NumberField = function(config){
35891     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35892 };
35893
35894 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35895     
35896     /**
35897      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35898      */
35899     allowDecimals : true,
35900     /**
35901      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35902      */
35903     decimalSeparator : ".",
35904     /**
35905      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35906      */
35907     decimalPrecision : 2,
35908     /**
35909      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35910      */
35911     allowNegative : true,
35912     
35913     /**
35914      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35915      */
35916     allowZero: true,
35917     /**
35918      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35919      */
35920     minValue : Number.NEGATIVE_INFINITY,
35921     /**
35922      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35923      */
35924     maxValue : Number.MAX_VALUE,
35925     /**
35926      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35927      */
35928     minText : "The minimum value for this field is {0}",
35929     /**
35930      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35931      */
35932     maxText : "The maximum value for this field is {0}",
35933     /**
35934      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35935      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35936      */
35937     nanText : "{0} is not a valid number",
35938     /**
35939      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35940      */
35941     thousandsDelimiter : false,
35942     /**
35943      * @cfg {String} valueAlign alignment of value
35944      */
35945     valueAlign : "left",
35946
35947     getAutoCreate : function()
35948     {
35949         var hiddenInput = {
35950             tag: 'input',
35951             type: 'hidden',
35952             id: Roo.id(),
35953             cls: 'hidden-number-input'
35954         };
35955         
35956         if (this.name) {
35957             hiddenInput.name = this.name;
35958         }
35959         
35960         this.name = '';
35961         
35962         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35963         
35964         this.name = hiddenInput.name;
35965         
35966         if(cfg.cn.length > 0) {
35967             cfg.cn.push(hiddenInput);
35968         }
35969         
35970         return cfg;
35971     },
35972
35973     // private
35974     initEvents : function()
35975     {   
35976         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35977         
35978         var allowed = "0123456789";
35979         
35980         if(this.allowDecimals){
35981             allowed += this.decimalSeparator;
35982         }
35983         
35984         if(this.allowNegative){
35985             allowed += "-";
35986         }
35987         
35988         if(this.thousandsDelimiter) {
35989             allowed += ",";
35990         }
35991         
35992         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35993         
35994         var keyPress = function(e){
35995             
35996             var k = e.getKey();
35997             
35998             var c = e.getCharCode();
35999             
36000             if(
36001                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36002                     allowed.indexOf(String.fromCharCode(c)) === -1
36003             ){
36004                 e.stopEvent();
36005                 return;
36006             }
36007             
36008             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36009                 return;
36010             }
36011             
36012             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36013                 e.stopEvent();
36014             }
36015         };
36016         
36017         this.el.on("keypress", keyPress, this);
36018     },
36019     
36020     validateValue : function(value)
36021     {
36022         
36023         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36024             return false;
36025         }
36026         
36027         var num = this.parseValue(value);
36028         
36029         if(isNaN(num)){
36030             this.markInvalid(String.format(this.nanText, value));
36031             return false;
36032         }
36033         
36034         if(num < this.minValue){
36035             this.markInvalid(String.format(this.minText, this.minValue));
36036             return false;
36037         }
36038         
36039         if(num > this.maxValue){
36040             this.markInvalid(String.format(this.maxText, this.maxValue));
36041             return false;
36042         }
36043         
36044         return true;
36045     },
36046
36047     getValue : function()
36048     {
36049         var v = this.hiddenEl().getValue();
36050         
36051         return this.fixPrecision(this.parseValue(v));
36052     },
36053
36054     parseValue : function(value)
36055     {
36056         if(this.thousandsDelimiter) {
36057             value += "";
36058             r = new RegExp(",", "g");
36059             value = value.replace(r, "");
36060         }
36061         
36062         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36063         return isNaN(value) ? '' : value;
36064     },
36065
36066     fixPrecision : function(value)
36067     {
36068         if(this.thousandsDelimiter) {
36069             value += "";
36070             r = new RegExp(",", "g");
36071             value = value.replace(r, "");
36072         }
36073         
36074         var nan = isNaN(value);
36075         
36076         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36077             return nan ? '' : value;
36078         }
36079         return parseFloat(value).toFixed(this.decimalPrecision);
36080     },
36081
36082     setValue : function(v)
36083     {
36084         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36085         
36086         this.value = v;
36087         
36088         if(this.rendered){
36089             
36090             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36091             
36092             this.inputEl().dom.value = (v == '') ? '' :
36093                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36094             
36095             if(!this.allowZero && v === '0') {
36096                 this.hiddenEl().dom.value = '';
36097                 this.inputEl().dom.value = '';
36098             }
36099             
36100             this.validate();
36101         }
36102     },
36103
36104     decimalPrecisionFcn : function(v)
36105     {
36106         return Math.floor(v);
36107     },
36108
36109     beforeBlur : function()
36110     {
36111         var v = this.parseValue(this.getRawValue());
36112         
36113         if(v || v === 0 || v === ''){
36114             this.setValue(v);
36115         }
36116     },
36117     
36118     hiddenEl : function()
36119     {
36120         return this.el.select('input.hidden-number-input',true).first();
36121     }
36122     
36123 });
36124
36125  
36126
36127 /*
36128 * Licence: LGPL
36129 */
36130
36131 /**
36132  * @class Roo.bootstrap.DocumentSlider
36133  * @extends Roo.bootstrap.Component
36134  * Bootstrap DocumentSlider class
36135  * 
36136  * @constructor
36137  * Create a new DocumentViewer
36138  * @param {Object} config The config object
36139  */
36140
36141 Roo.bootstrap.DocumentSlider = function(config){
36142     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36143     
36144     this.files = [];
36145     
36146     this.addEvents({
36147         /**
36148          * @event initial
36149          * Fire after initEvent
36150          * @param {Roo.bootstrap.DocumentSlider} this
36151          */
36152         "initial" : true,
36153         /**
36154          * @event update
36155          * Fire after update
36156          * @param {Roo.bootstrap.DocumentSlider} this
36157          */
36158         "update" : true,
36159         /**
36160          * @event click
36161          * Fire after click
36162          * @param {Roo.bootstrap.DocumentSlider} this
36163          */
36164         "click" : true
36165     });
36166 };
36167
36168 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36169     
36170     files : false,
36171     
36172     indicator : 0,
36173     
36174     getAutoCreate : function()
36175     {
36176         var cfg = {
36177             tag : 'div',
36178             cls : 'roo-document-slider',
36179             cn : [
36180                 {
36181                     tag : 'div',
36182                     cls : 'roo-document-slider-header',
36183                     cn : [
36184                         {
36185                             tag : 'div',
36186                             cls : 'roo-document-slider-header-title'
36187                         }
36188                     ]
36189                 },
36190                 {
36191                     tag : 'div',
36192                     cls : 'roo-document-slider-body',
36193                     cn : [
36194                         {
36195                             tag : 'div',
36196                             cls : 'roo-document-slider-prev',
36197                             cn : [
36198                                 {
36199                                     tag : 'i',
36200                                     cls : 'fa fa-chevron-left'
36201                                 }
36202                             ]
36203                         },
36204                         {
36205                             tag : 'div',
36206                             cls : 'roo-document-slider-thumb',
36207                             cn : [
36208                                 {
36209                                     tag : 'img',
36210                                     cls : 'roo-document-slider-image'
36211                                 }
36212                             ]
36213                         },
36214                         {
36215                             tag : 'div',
36216                             cls : 'roo-document-slider-next',
36217                             cn : [
36218                                 {
36219                                     tag : 'i',
36220                                     cls : 'fa fa-chevron-right'
36221                                 }
36222                             ]
36223                         }
36224                     ]
36225                 }
36226             ]
36227         };
36228         
36229         return cfg;
36230     },
36231     
36232     initEvents : function()
36233     {
36234         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36235         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36236         
36237         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36238         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36239         
36240         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36241         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36242         
36243         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36244         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36245         
36246         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36247         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36248         
36249         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36250         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36251         
36252         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36253         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36254         
36255         this.thumbEl.on('click', this.onClick, this);
36256         
36257         this.prevIndicator.on('click', this.prev, this);
36258         
36259         this.nextIndicator.on('click', this.next, this);
36260         
36261     },
36262     
36263     initial : function()
36264     {
36265         if(this.files.length){
36266             this.indicator = 1;
36267             this.update()
36268         }
36269         
36270         this.fireEvent('initial', this);
36271     },
36272     
36273     update : function()
36274     {
36275         this.imageEl.attr('src', this.files[this.indicator - 1]);
36276         
36277         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36278         
36279         this.prevIndicator.show();
36280         
36281         if(this.indicator == 1){
36282             this.prevIndicator.hide();
36283         }
36284         
36285         this.nextIndicator.show();
36286         
36287         if(this.indicator == this.files.length){
36288             this.nextIndicator.hide();
36289         }
36290         
36291         this.thumbEl.scrollTo('top');
36292         
36293         this.fireEvent('update', this);
36294     },
36295     
36296     onClick : function(e)
36297     {
36298         e.preventDefault();
36299         
36300         this.fireEvent('click', this);
36301     },
36302     
36303     prev : function(e)
36304     {
36305         e.preventDefault();
36306         
36307         this.indicator = Math.max(1, this.indicator - 1);
36308         
36309         this.update();
36310     },
36311     
36312     next : function(e)
36313     {
36314         e.preventDefault();
36315         
36316         this.indicator = Math.min(this.files.length, this.indicator + 1);
36317         
36318         this.update();
36319     }
36320 });
36321 /*
36322  * - LGPL
36323  *
36324  * RadioSet
36325  *
36326  *
36327  */
36328
36329 /**
36330  * @class Roo.bootstrap.RadioSet
36331  * @extends Roo.bootstrap.Input
36332  * Bootstrap RadioSet class
36333  * @cfg {String} indicatorpos (left|right) default left
36334  * @cfg {Boolean} inline (true|false) inline the element (default true)
36335  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36336  * @constructor
36337  * Create a new RadioSet
36338  * @param {Object} config The config object
36339  */
36340
36341 Roo.bootstrap.RadioSet = function(config){
36342     
36343     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36344     
36345     this.radioes = [];
36346     
36347     Roo.bootstrap.RadioSet.register(this);
36348     
36349     this.addEvents({
36350         /**
36351         * @event check
36352         * Fires when the element is checked or unchecked.
36353         * @param {Roo.bootstrap.RadioSet} this This radio
36354         * @param {Roo.bootstrap.Radio} item The checked item
36355         */
36356        check : true,
36357        /**
36358         * @event click
36359         * Fires when the element is click.
36360         * @param {Roo.bootstrap.RadioSet} this This radio set
36361         * @param {Roo.bootstrap.Radio} item The checked item
36362         * @param {Roo.EventObject} e The event object
36363         */
36364        click : true
36365     });
36366     
36367 };
36368
36369 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36370
36371     radioes : false,
36372     
36373     inline : true,
36374     
36375     weight : '',
36376     
36377     indicatorpos : 'left',
36378     
36379     getAutoCreate : function()
36380     {
36381         var label = {
36382             tag : 'label',
36383             cls : 'roo-radio-set-label',
36384             cn : [
36385                 {
36386                     tag : 'span',
36387                     html : this.fieldLabel
36388                 }
36389             ]
36390         };
36391         if (Roo.bootstrap.version == 3) {
36392             
36393             
36394             if(this.indicatorpos == 'left'){
36395                 label.cn.unshift({
36396                     tag : 'i',
36397                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36398                     tooltip : 'This field is required'
36399                 });
36400             } else {
36401                 label.cn.push({
36402                     tag : 'i',
36403                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36404                     tooltip : 'This field is required'
36405                 });
36406             }
36407         }
36408         var items = {
36409             tag : 'div',
36410             cls : 'roo-radio-set-items'
36411         };
36412         
36413         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36414         
36415         if (align === 'left' && this.fieldLabel.length) {
36416             
36417             items = {
36418                 cls : "roo-radio-set-right", 
36419                 cn: [
36420                     items
36421                 ]
36422             };
36423             
36424             if(this.labelWidth > 12){
36425                 label.style = "width: " + this.labelWidth + 'px';
36426             }
36427             
36428             if(this.labelWidth < 13 && this.labelmd == 0){
36429                 this.labelmd = this.labelWidth;
36430             }
36431             
36432             if(this.labellg > 0){
36433                 label.cls += ' col-lg-' + this.labellg;
36434                 items.cls += ' col-lg-' + (12 - this.labellg);
36435             }
36436             
36437             if(this.labelmd > 0){
36438                 label.cls += ' col-md-' + this.labelmd;
36439                 items.cls += ' col-md-' + (12 - this.labelmd);
36440             }
36441             
36442             if(this.labelsm > 0){
36443                 label.cls += ' col-sm-' + this.labelsm;
36444                 items.cls += ' col-sm-' + (12 - this.labelsm);
36445             }
36446             
36447             if(this.labelxs > 0){
36448                 label.cls += ' col-xs-' + this.labelxs;
36449                 items.cls += ' col-xs-' + (12 - this.labelxs);
36450             }
36451         }
36452         
36453         var cfg = {
36454             tag : 'div',
36455             cls : 'roo-radio-set',
36456             cn : [
36457                 {
36458                     tag : 'input',
36459                     cls : 'roo-radio-set-input',
36460                     type : 'hidden',
36461                     name : this.name,
36462                     value : this.value ? this.value :  ''
36463                 },
36464                 label,
36465                 items
36466             ]
36467         };
36468         
36469         if(this.weight.length){
36470             cfg.cls += ' roo-radio-' + this.weight;
36471         }
36472         
36473         if(this.inline) {
36474             cfg.cls += ' roo-radio-set-inline';
36475         }
36476         
36477         var settings=this;
36478         ['xs','sm','md','lg'].map(function(size){
36479             if (settings[size]) {
36480                 cfg.cls += ' col-' + size + '-' + settings[size];
36481             }
36482         });
36483         
36484         return cfg;
36485         
36486     },
36487
36488     initEvents : function()
36489     {
36490         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36491         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36492         
36493         if(!this.fieldLabel.length){
36494             this.labelEl.hide();
36495         }
36496         
36497         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36498         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36499         
36500         this.indicator = this.indicatorEl();
36501         
36502         if(this.indicator){
36503             this.indicator.addClass('invisible');
36504         }
36505         
36506         this.originalValue = this.getValue();
36507         
36508     },
36509     
36510     inputEl: function ()
36511     {
36512         return this.el.select('.roo-radio-set-input', true).first();
36513     },
36514     
36515     getChildContainer : function()
36516     {
36517         return this.itemsEl;
36518     },
36519     
36520     register : function(item)
36521     {
36522         this.radioes.push(item);
36523         
36524     },
36525     
36526     validate : function()
36527     {   
36528         if(this.getVisibilityEl().hasClass('hidden')){
36529             return true;
36530         }
36531         
36532         var valid = false;
36533         
36534         Roo.each(this.radioes, function(i){
36535             if(!i.checked){
36536                 return;
36537             }
36538             
36539             valid = true;
36540             return false;
36541         });
36542         
36543         if(this.allowBlank) {
36544             return true;
36545         }
36546         
36547         if(this.disabled || valid){
36548             this.markValid();
36549             return true;
36550         }
36551         
36552         this.markInvalid();
36553         return false;
36554         
36555     },
36556     
36557     markValid : function()
36558     {
36559         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36560             this.indicatorEl().removeClass('visible');
36561             this.indicatorEl().addClass('invisible');
36562         }
36563         
36564         
36565         if (Roo.bootstrap.version == 3) {
36566             this.el.removeClass([this.invalidClass, this.validClass]);
36567             this.el.addClass(this.validClass);
36568         } else {
36569             this.el.removeClass(['is-invalid','is-valid']);
36570             this.el.addClass(['is-valid']);
36571         }
36572         this.fireEvent('valid', this);
36573     },
36574     
36575     markInvalid : function(msg)
36576     {
36577         if(this.allowBlank || this.disabled){
36578             return;
36579         }
36580         
36581         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36582             this.indicatorEl().removeClass('invisible');
36583             this.indicatorEl().addClass('visible');
36584         }
36585         if (Roo.bootstrap.version == 3) {
36586             this.el.removeClass([this.invalidClass, this.validClass]);
36587             this.el.addClass(this.invalidClass);
36588         } else {
36589             this.el.removeClass(['is-invalid','is-valid']);
36590             this.el.addClass(['is-invalid']);
36591         }
36592         
36593         this.fireEvent('invalid', this, msg);
36594         
36595     },
36596     
36597     setValue : function(v, suppressEvent)
36598     {   
36599         if(this.value === v){
36600             return;
36601         }
36602         
36603         this.value = v;
36604         
36605         if(this.rendered){
36606             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36607         }
36608         
36609         Roo.each(this.radioes, function(i){
36610             i.checked = false;
36611             i.el.removeClass('checked');
36612         });
36613         
36614         Roo.each(this.radioes, function(i){
36615             
36616             if(i.value === v || i.value.toString() === v.toString()){
36617                 i.checked = true;
36618                 i.el.addClass('checked');
36619                 
36620                 if(suppressEvent !== true){
36621                     this.fireEvent('check', this, i);
36622                 }
36623                 
36624                 return false;
36625             }
36626             
36627         }, this);
36628         
36629         this.validate();
36630     },
36631     
36632     clearInvalid : function(){
36633         
36634         if(!this.el || this.preventMark){
36635             return;
36636         }
36637         
36638         this.el.removeClass([this.invalidClass]);
36639         
36640         this.fireEvent('valid', this);
36641     }
36642     
36643 });
36644
36645 Roo.apply(Roo.bootstrap.RadioSet, {
36646     
36647     groups: {},
36648     
36649     register : function(set)
36650     {
36651         this.groups[set.name] = set;
36652     },
36653     
36654     get: function(name) 
36655     {
36656         if (typeof(this.groups[name]) == 'undefined') {
36657             return false;
36658         }
36659         
36660         return this.groups[name] ;
36661     }
36662     
36663 });
36664 /*
36665  * Based on:
36666  * Ext JS Library 1.1.1
36667  * Copyright(c) 2006-2007, Ext JS, LLC.
36668  *
36669  * Originally Released Under LGPL - original licence link has changed is not relivant.
36670  *
36671  * Fork - LGPL
36672  * <script type="text/javascript">
36673  */
36674
36675
36676 /**
36677  * @class Roo.bootstrap.SplitBar
36678  * @extends Roo.util.Observable
36679  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36680  * <br><br>
36681  * Usage:
36682  * <pre><code>
36683 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36684                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36685 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36686 split.minSize = 100;
36687 split.maxSize = 600;
36688 split.animate = true;
36689 split.on('moved', splitterMoved);
36690 </code></pre>
36691  * @constructor
36692  * Create a new SplitBar
36693  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36694  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36695  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36696  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36697                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36698                         position of the SplitBar).
36699  */
36700 Roo.bootstrap.SplitBar = function(cfg){
36701     
36702     /** @private */
36703     
36704     //{
36705     //  dragElement : elm
36706     //  resizingElement: el,
36707         // optional..
36708     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36709     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36710         // existingProxy ???
36711     //}
36712     
36713     this.el = Roo.get(cfg.dragElement, true);
36714     this.el.dom.unselectable = "on";
36715     /** @private */
36716     this.resizingEl = Roo.get(cfg.resizingElement, true);
36717
36718     /**
36719      * @private
36720      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36721      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36722      * @type Number
36723      */
36724     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36725     
36726     /**
36727      * The minimum size of the resizing element. (Defaults to 0)
36728      * @type Number
36729      */
36730     this.minSize = 0;
36731     
36732     /**
36733      * The maximum size of the resizing element. (Defaults to 2000)
36734      * @type Number
36735      */
36736     this.maxSize = 2000;
36737     
36738     /**
36739      * Whether to animate the transition to the new size
36740      * @type Boolean
36741      */
36742     this.animate = false;
36743     
36744     /**
36745      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36746      * @type Boolean
36747      */
36748     this.useShim = false;
36749     
36750     /** @private */
36751     this.shim = null;
36752     
36753     if(!cfg.existingProxy){
36754         /** @private */
36755         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36756     }else{
36757         this.proxy = Roo.get(cfg.existingProxy).dom;
36758     }
36759     /** @private */
36760     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36761     
36762     /** @private */
36763     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36764     
36765     /** @private */
36766     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36767     
36768     /** @private */
36769     this.dragSpecs = {};
36770     
36771     /**
36772      * @private The adapter to use to positon and resize elements
36773      */
36774     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36775     this.adapter.init(this);
36776     
36777     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36778         /** @private */
36779         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36780         this.el.addClass("roo-splitbar-h");
36781     }else{
36782         /** @private */
36783         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36784         this.el.addClass("roo-splitbar-v");
36785     }
36786     
36787     this.addEvents({
36788         /**
36789          * @event resize
36790          * Fires when the splitter is moved (alias for {@link #event-moved})
36791          * @param {Roo.bootstrap.SplitBar} this
36792          * @param {Number} newSize the new width or height
36793          */
36794         "resize" : true,
36795         /**
36796          * @event moved
36797          * Fires when the splitter is moved
36798          * @param {Roo.bootstrap.SplitBar} this
36799          * @param {Number} newSize the new width or height
36800          */
36801         "moved" : true,
36802         /**
36803          * @event beforeresize
36804          * Fires before the splitter is dragged
36805          * @param {Roo.bootstrap.SplitBar} this
36806          */
36807         "beforeresize" : true,
36808
36809         "beforeapply" : true
36810     });
36811
36812     Roo.util.Observable.call(this);
36813 };
36814
36815 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36816     onStartProxyDrag : function(x, y){
36817         this.fireEvent("beforeresize", this);
36818         if(!this.overlay){
36819             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36820             o.unselectable();
36821             o.enableDisplayMode("block");
36822             // all splitbars share the same overlay
36823             Roo.bootstrap.SplitBar.prototype.overlay = o;
36824         }
36825         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36826         this.overlay.show();
36827         Roo.get(this.proxy).setDisplayed("block");
36828         var size = this.adapter.getElementSize(this);
36829         this.activeMinSize = this.getMinimumSize();;
36830         this.activeMaxSize = this.getMaximumSize();;
36831         var c1 = size - this.activeMinSize;
36832         var c2 = Math.max(this.activeMaxSize - size, 0);
36833         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36834             this.dd.resetConstraints();
36835             this.dd.setXConstraint(
36836                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36837                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36838             );
36839             this.dd.setYConstraint(0, 0);
36840         }else{
36841             this.dd.resetConstraints();
36842             this.dd.setXConstraint(0, 0);
36843             this.dd.setYConstraint(
36844                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36845                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36846             );
36847          }
36848         this.dragSpecs.startSize = size;
36849         this.dragSpecs.startPoint = [x, y];
36850         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36851     },
36852     
36853     /** 
36854      * @private Called after the drag operation by the DDProxy
36855      */
36856     onEndProxyDrag : function(e){
36857         Roo.get(this.proxy).setDisplayed(false);
36858         var endPoint = Roo.lib.Event.getXY(e);
36859         if(this.overlay){
36860             this.overlay.hide();
36861         }
36862         var newSize;
36863         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36864             newSize = this.dragSpecs.startSize + 
36865                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36866                     endPoint[0] - this.dragSpecs.startPoint[0] :
36867                     this.dragSpecs.startPoint[0] - endPoint[0]
36868                 );
36869         }else{
36870             newSize = this.dragSpecs.startSize + 
36871                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36872                     endPoint[1] - this.dragSpecs.startPoint[1] :
36873                     this.dragSpecs.startPoint[1] - endPoint[1]
36874                 );
36875         }
36876         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36877         if(newSize != this.dragSpecs.startSize){
36878             if(this.fireEvent('beforeapply', this, newSize) !== false){
36879                 this.adapter.setElementSize(this, newSize);
36880                 this.fireEvent("moved", this, newSize);
36881                 this.fireEvent("resize", this, newSize);
36882             }
36883         }
36884     },
36885     
36886     /**
36887      * Get the adapter this SplitBar uses
36888      * @return The adapter object
36889      */
36890     getAdapter : function(){
36891         return this.adapter;
36892     },
36893     
36894     /**
36895      * Set the adapter this SplitBar uses
36896      * @param {Object} adapter A SplitBar adapter object
36897      */
36898     setAdapter : function(adapter){
36899         this.adapter = adapter;
36900         this.adapter.init(this);
36901     },
36902     
36903     /**
36904      * Gets the minimum size for the resizing element
36905      * @return {Number} The minimum size
36906      */
36907     getMinimumSize : function(){
36908         return this.minSize;
36909     },
36910     
36911     /**
36912      * Sets the minimum size for the resizing element
36913      * @param {Number} minSize The minimum size
36914      */
36915     setMinimumSize : function(minSize){
36916         this.minSize = minSize;
36917     },
36918     
36919     /**
36920      * Gets the maximum size for the resizing element
36921      * @return {Number} The maximum size
36922      */
36923     getMaximumSize : function(){
36924         return this.maxSize;
36925     },
36926     
36927     /**
36928      * Sets the maximum size for the resizing element
36929      * @param {Number} maxSize The maximum size
36930      */
36931     setMaximumSize : function(maxSize){
36932         this.maxSize = maxSize;
36933     },
36934     
36935     /**
36936      * Sets the initialize size for the resizing element
36937      * @param {Number} size The initial size
36938      */
36939     setCurrentSize : function(size){
36940         var oldAnimate = this.animate;
36941         this.animate = false;
36942         this.adapter.setElementSize(this, size);
36943         this.animate = oldAnimate;
36944     },
36945     
36946     /**
36947      * Destroy this splitbar. 
36948      * @param {Boolean} removeEl True to remove the element
36949      */
36950     destroy : function(removeEl){
36951         if(this.shim){
36952             this.shim.remove();
36953         }
36954         this.dd.unreg();
36955         this.proxy.parentNode.removeChild(this.proxy);
36956         if(removeEl){
36957             this.el.remove();
36958         }
36959     }
36960 });
36961
36962 /**
36963  * @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.
36964  */
36965 Roo.bootstrap.SplitBar.createProxy = function(dir){
36966     var proxy = new Roo.Element(document.createElement("div"));
36967     proxy.unselectable();
36968     var cls = 'roo-splitbar-proxy';
36969     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36970     document.body.appendChild(proxy.dom);
36971     return proxy.dom;
36972 };
36973
36974 /** 
36975  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36976  * Default Adapter. It assumes the splitter and resizing element are not positioned
36977  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36978  */
36979 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36980 };
36981
36982 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36983     // do nothing for now
36984     init : function(s){
36985     
36986     },
36987     /**
36988      * Called before drag operations to get the current size of the resizing element. 
36989      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36990      */
36991      getElementSize : function(s){
36992         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36993             return s.resizingEl.getWidth();
36994         }else{
36995             return s.resizingEl.getHeight();
36996         }
36997     },
36998     
36999     /**
37000      * Called after drag operations to set the size of the resizing element.
37001      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37002      * @param {Number} newSize The new size to set
37003      * @param {Function} onComplete A function to be invoked when resizing is complete
37004      */
37005     setElementSize : function(s, newSize, onComplete){
37006         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37007             if(!s.animate){
37008                 s.resizingEl.setWidth(newSize);
37009                 if(onComplete){
37010                     onComplete(s, newSize);
37011                 }
37012             }else{
37013                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37014             }
37015         }else{
37016             
37017             if(!s.animate){
37018                 s.resizingEl.setHeight(newSize);
37019                 if(onComplete){
37020                     onComplete(s, newSize);
37021                 }
37022             }else{
37023                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37024             }
37025         }
37026     }
37027 };
37028
37029 /** 
37030  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37031  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37032  * Adapter that  moves the splitter element to align with the resized sizing element. 
37033  * Used with an absolute positioned SplitBar.
37034  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37035  * document.body, make sure you assign an id to the body element.
37036  */
37037 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37038     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37039     this.container = Roo.get(container);
37040 };
37041
37042 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37043     init : function(s){
37044         this.basic.init(s);
37045     },
37046     
37047     getElementSize : function(s){
37048         return this.basic.getElementSize(s);
37049     },
37050     
37051     setElementSize : function(s, newSize, onComplete){
37052         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37053     },
37054     
37055     moveSplitter : function(s){
37056         var yes = Roo.bootstrap.SplitBar;
37057         switch(s.placement){
37058             case yes.LEFT:
37059                 s.el.setX(s.resizingEl.getRight());
37060                 break;
37061             case yes.RIGHT:
37062                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37063                 break;
37064             case yes.TOP:
37065                 s.el.setY(s.resizingEl.getBottom());
37066                 break;
37067             case yes.BOTTOM:
37068                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37069                 break;
37070         }
37071     }
37072 };
37073
37074 /**
37075  * Orientation constant - Create a vertical SplitBar
37076  * @static
37077  * @type Number
37078  */
37079 Roo.bootstrap.SplitBar.VERTICAL = 1;
37080
37081 /**
37082  * Orientation constant - Create a horizontal SplitBar
37083  * @static
37084  * @type Number
37085  */
37086 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37087
37088 /**
37089  * Placement constant - The resizing element is to the left of the splitter element
37090  * @static
37091  * @type Number
37092  */
37093 Roo.bootstrap.SplitBar.LEFT = 1;
37094
37095 /**
37096  * Placement constant - The resizing element is to the right of the splitter element
37097  * @static
37098  * @type Number
37099  */
37100 Roo.bootstrap.SplitBar.RIGHT = 2;
37101
37102 /**
37103  * Placement constant - The resizing element is positioned above the splitter element
37104  * @static
37105  * @type Number
37106  */
37107 Roo.bootstrap.SplitBar.TOP = 3;
37108
37109 /**
37110  * Placement constant - The resizing element is positioned under splitter element
37111  * @static
37112  * @type Number
37113  */
37114 Roo.bootstrap.SplitBar.BOTTOM = 4;
37115 Roo.namespace("Roo.bootstrap.layout");/*
37116  * Based on:
37117  * Ext JS Library 1.1.1
37118  * Copyright(c) 2006-2007, Ext JS, LLC.
37119  *
37120  * Originally Released Under LGPL - original licence link has changed is not relivant.
37121  *
37122  * Fork - LGPL
37123  * <script type="text/javascript">
37124  */
37125
37126 /**
37127  * @class Roo.bootstrap.layout.Manager
37128  * @extends Roo.bootstrap.Component
37129  * Base class for layout managers.
37130  */
37131 Roo.bootstrap.layout.Manager = function(config)
37132 {
37133     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37134
37135
37136
37137
37138
37139     /** false to disable window resize monitoring @type Boolean */
37140     this.monitorWindowResize = true;
37141     this.regions = {};
37142     this.addEvents({
37143         /**
37144          * @event layout
37145          * Fires when a layout is performed.
37146          * @param {Roo.LayoutManager} this
37147          */
37148         "layout" : true,
37149         /**
37150          * @event regionresized
37151          * Fires when the user resizes a region.
37152          * @param {Roo.LayoutRegion} region The resized region
37153          * @param {Number} newSize The new size (width for east/west, height for north/south)
37154          */
37155         "regionresized" : true,
37156         /**
37157          * @event regioncollapsed
37158          * Fires when a region is collapsed.
37159          * @param {Roo.LayoutRegion} region The collapsed region
37160          */
37161         "regioncollapsed" : true,
37162         /**
37163          * @event regionexpanded
37164          * Fires when a region is expanded.
37165          * @param {Roo.LayoutRegion} region The expanded region
37166          */
37167         "regionexpanded" : true
37168     });
37169     this.updating = false;
37170
37171     if (config.el) {
37172         this.el = Roo.get(config.el);
37173         this.initEvents();
37174     }
37175
37176 };
37177
37178 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37179
37180
37181     regions : null,
37182
37183     monitorWindowResize : true,
37184
37185
37186     updating : false,
37187
37188
37189     onRender : function(ct, position)
37190     {
37191         if(!this.el){
37192             this.el = Roo.get(ct);
37193             this.initEvents();
37194         }
37195         //this.fireEvent('render',this);
37196     },
37197
37198
37199     initEvents: function()
37200     {
37201
37202
37203         // ie scrollbar fix
37204         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37205             document.body.scroll = "no";
37206         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37207             this.el.position('relative');
37208         }
37209         this.id = this.el.id;
37210         this.el.addClass("roo-layout-container");
37211         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37212         if(this.el.dom != document.body ) {
37213             this.el.on('resize', this.layout,this);
37214             this.el.on('show', this.layout,this);
37215         }
37216
37217     },
37218
37219     /**
37220      * Returns true if this layout is currently being updated
37221      * @return {Boolean}
37222      */
37223     isUpdating : function(){
37224         return this.updating;
37225     },
37226
37227     /**
37228      * Suspend the LayoutManager from doing auto-layouts while
37229      * making multiple add or remove calls
37230      */
37231     beginUpdate : function(){
37232         this.updating = true;
37233     },
37234
37235     /**
37236      * Restore auto-layouts and optionally disable the manager from performing a layout
37237      * @param {Boolean} noLayout true to disable a layout update
37238      */
37239     endUpdate : function(noLayout){
37240         this.updating = false;
37241         if(!noLayout){
37242             this.layout();
37243         }
37244     },
37245
37246     layout: function(){
37247         // abstract...
37248     },
37249
37250     onRegionResized : function(region, newSize){
37251         this.fireEvent("regionresized", region, newSize);
37252         this.layout();
37253     },
37254
37255     onRegionCollapsed : function(region){
37256         this.fireEvent("regioncollapsed", region);
37257     },
37258
37259     onRegionExpanded : function(region){
37260         this.fireEvent("regionexpanded", region);
37261     },
37262
37263     /**
37264      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37265      * performs box-model adjustments.
37266      * @return {Object} The size as an object {width: (the width), height: (the height)}
37267      */
37268     getViewSize : function()
37269     {
37270         var size;
37271         if(this.el.dom != document.body){
37272             size = this.el.getSize();
37273         }else{
37274             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37275         }
37276         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37277         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37278         return size;
37279     },
37280
37281     /**
37282      * Returns the Element this layout is bound to.
37283      * @return {Roo.Element}
37284      */
37285     getEl : function(){
37286         return this.el;
37287     },
37288
37289     /**
37290      * Returns the specified region.
37291      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37292      * @return {Roo.LayoutRegion}
37293      */
37294     getRegion : function(target){
37295         return this.regions[target.toLowerCase()];
37296     },
37297
37298     onWindowResize : function(){
37299         if(this.monitorWindowResize){
37300             this.layout();
37301         }
37302     }
37303 });
37304 /*
37305  * Based on:
37306  * Ext JS Library 1.1.1
37307  * Copyright(c) 2006-2007, Ext JS, LLC.
37308  *
37309  * Originally Released Under LGPL - original licence link has changed is not relivant.
37310  *
37311  * Fork - LGPL
37312  * <script type="text/javascript">
37313  */
37314 /**
37315  * @class Roo.bootstrap.layout.Border
37316  * @extends Roo.bootstrap.layout.Manager
37317  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37318  * please see: examples/bootstrap/nested.html<br><br>
37319  
37320 <b>The container the layout is rendered into can be either the body element or any other element.
37321 If it is not the body element, the container needs to either be an absolute positioned element,
37322 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37323 the container size if it is not the body element.</b>
37324
37325 * @constructor
37326 * Create a new Border
37327 * @param {Object} config Configuration options
37328  */
37329 Roo.bootstrap.layout.Border = function(config){
37330     config = config || {};
37331     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37332     
37333     
37334     
37335     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37336         if(config[region]){
37337             config[region].region = region;
37338             this.addRegion(config[region]);
37339         }
37340     },this);
37341     
37342 };
37343
37344 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37345
37346 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37347     
37348     parent : false, // this might point to a 'nest' or a ???
37349     
37350     /**
37351      * Creates and adds a new region if it doesn't already exist.
37352      * @param {String} target The target region key (north, south, east, west or center).
37353      * @param {Object} config The regions config object
37354      * @return {BorderLayoutRegion} The new region
37355      */
37356     addRegion : function(config)
37357     {
37358         if(!this.regions[config.region]){
37359             var r = this.factory(config);
37360             this.bindRegion(r);
37361         }
37362         return this.regions[config.region];
37363     },
37364
37365     // private (kinda)
37366     bindRegion : function(r){
37367         this.regions[r.config.region] = r;
37368         
37369         r.on("visibilitychange",    this.layout, this);
37370         r.on("paneladded",          this.layout, this);
37371         r.on("panelremoved",        this.layout, this);
37372         r.on("invalidated",         this.layout, this);
37373         r.on("resized",             this.onRegionResized, this);
37374         r.on("collapsed",           this.onRegionCollapsed, this);
37375         r.on("expanded",            this.onRegionExpanded, this);
37376     },
37377
37378     /**
37379      * Performs a layout update.
37380      */
37381     layout : function()
37382     {
37383         if(this.updating) {
37384             return;
37385         }
37386         
37387         // render all the rebions if they have not been done alreayd?
37388         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37389             if(this.regions[region] && !this.regions[region].bodyEl){
37390                 this.regions[region].onRender(this.el)
37391             }
37392         },this);
37393         
37394         var size = this.getViewSize();
37395         var w = size.width;
37396         var h = size.height;
37397         var centerW = w;
37398         var centerH = h;
37399         var centerY = 0;
37400         var centerX = 0;
37401         //var x = 0, y = 0;
37402
37403         var rs = this.regions;
37404         var north = rs["north"];
37405         var south = rs["south"]; 
37406         var west = rs["west"];
37407         var east = rs["east"];
37408         var center = rs["center"];
37409         //if(this.hideOnLayout){ // not supported anymore
37410             //c.el.setStyle("display", "none");
37411         //}
37412         if(north && north.isVisible()){
37413             var b = north.getBox();
37414             var m = north.getMargins();
37415             b.width = w - (m.left+m.right);
37416             b.x = m.left;
37417             b.y = m.top;
37418             centerY = b.height + b.y + m.bottom;
37419             centerH -= centerY;
37420             north.updateBox(this.safeBox(b));
37421         }
37422         if(south && south.isVisible()){
37423             var b = south.getBox();
37424             var m = south.getMargins();
37425             b.width = w - (m.left+m.right);
37426             b.x = m.left;
37427             var totalHeight = (b.height + m.top + m.bottom);
37428             b.y = h - totalHeight + m.top;
37429             centerH -= totalHeight;
37430             south.updateBox(this.safeBox(b));
37431         }
37432         if(west && west.isVisible()){
37433             var b = west.getBox();
37434             var m = west.getMargins();
37435             b.height = centerH - (m.top+m.bottom);
37436             b.x = m.left;
37437             b.y = centerY + m.top;
37438             var totalWidth = (b.width + m.left + m.right);
37439             centerX += totalWidth;
37440             centerW -= totalWidth;
37441             west.updateBox(this.safeBox(b));
37442         }
37443         if(east && east.isVisible()){
37444             var b = east.getBox();
37445             var m = east.getMargins();
37446             b.height = centerH - (m.top+m.bottom);
37447             var totalWidth = (b.width + m.left + m.right);
37448             b.x = w - totalWidth + m.left;
37449             b.y = centerY + m.top;
37450             centerW -= totalWidth;
37451             east.updateBox(this.safeBox(b));
37452         }
37453         if(center){
37454             var m = center.getMargins();
37455             var centerBox = {
37456                 x: centerX + m.left,
37457                 y: centerY + m.top,
37458                 width: centerW - (m.left+m.right),
37459                 height: centerH - (m.top+m.bottom)
37460             };
37461             //if(this.hideOnLayout){
37462                 //center.el.setStyle("display", "block");
37463             //}
37464             center.updateBox(this.safeBox(centerBox));
37465         }
37466         this.el.repaint();
37467         this.fireEvent("layout", this);
37468     },
37469
37470     // private
37471     safeBox : function(box){
37472         box.width = Math.max(0, box.width);
37473         box.height = Math.max(0, box.height);
37474         return box;
37475     },
37476
37477     /**
37478      * Adds a ContentPanel (or subclass) to this layout.
37479      * @param {String} target The target region key (north, south, east, west or center).
37480      * @param {Roo.ContentPanel} panel The panel to add
37481      * @return {Roo.ContentPanel} The added panel
37482      */
37483     add : function(target, panel){
37484          
37485         target = target.toLowerCase();
37486         return this.regions[target].add(panel);
37487     },
37488
37489     /**
37490      * Remove a ContentPanel (or subclass) to this layout.
37491      * @param {String} target The target region key (north, south, east, west or center).
37492      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37493      * @return {Roo.ContentPanel} The removed panel
37494      */
37495     remove : function(target, panel){
37496         target = target.toLowerCase();
37497         return this.regions[target].remove(panel);
37498     },
37499
37500     /**
37501      * Searches all regions for a panel with the specified id
37502      * @param {String} panelId
37503      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37504      */
37505     findPanel : function(panelId){
37506         var rs = this.regions;
37507         for(var target in rs){
37508             if(typeof rs[target] != "function"){
37509                 var p = rs[target].getPanel(panelId);
37510                 if(p){
37511                     return p;
37512                 }
37513             }
37514         }
37515         return null;
37516     },
37517
37518     /**
37519      * Searches all regions for a panel with the specified id and activates (shows) it.
37520      * @param {String/ContentPanel} panelId The panels id or the panel itself
37521      * @return {Roo.ContentPanel} The shown panel or null
37522      */
37523     showPanel : function(panelId) {
37524       var rs = this.regions;
37525       for(var target in rs){
37526          var r = rs[target];
37527          if(typeof r != "function"){
37528             if(r.hasPanel(panelId)){
37529                return r.showPanel(panelId);
37530             }
37531          }
37532       }
37533       return null;
37534    },
37535
37536    /**
37537      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37538      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37539      */
37540    /*
37541     restoreState : function(provider){
37542         if(!provider){
37543             provider = Roo.state.Manager;
37544         }
37545         var sm = new Roo.LayoutStateManager();
37546         sm.init(this, provider);
37547     },
37548 */
37549  
37550  
37551     /**
37552      * Adds a xtype elements to the layout.
37553      * <pre><code>
37554
37555 layout.addxtype({
37556        xtype : 'ContentPanel',
37557        region: 'west',
37558        items: [ .... ]
37559    }
37560 );
37561
37562 layout.addxtype({
37563         xtype : 'NestedLayoutPanel',
37564         region: 'west',
37565         layout: {
37566            center: { },
37567            west: { }   
37568         },
37569         items : [ ... list of content panels or nested layout panels.. ]
37570    }
37571 );
37572 </code></pre>
37573      * @param {Object} cfg Xtype definition of item to add.
37574      */
37575     addxtype : function(cfg)
37576     {
37577         // basically accepts a pannel...
37578         // can accept a layout region..!?!?
37579         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37580         
37581         
37582         // theory?  children can only be panels??
37583         
37584         //if (!cfg.xtype.match(/Panel$/)) {
37585         //    return false;
37586         //}
37587         var ret = false;
37588         
37589         if (typeof(cfg.region) == 'undefined') {
37590             Roo.log("Failed to add Panel, region was not set");
37591             Roo.log(cfg);
37592             return false;
37593         }
37594         var region = cfg.region;
37595         delete cfg.region;
37596         
37597           
37598         var xitems = [];
37599         if (cfg.items) {
37600             xitems = cfg.items;
37601             delete cfg.items;
37602         }
37603         var nb = false;
37604         
37605         if ( region == 'center') {
37606             Roo.log("Center: " + cfg.title);
37607         }
37608         
37609         
37610         switch(cfg.xtype) 
37611         {
37612             case 'Content':  // ContentPanel (el, cfg)
37613             case 'Scroll':  // ContentPanel (el, cfg)
37614             case 'View': 
37615                 cfg.autoCreate = cfg.autoCreate || true;
37616                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37617                 //} else {
37618                 //    var el = this.el.createChild();
37619                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37620                 //}
37621                 
37622                 this.add(region, ret);
37623                 break;
37624             
37625             /*
37626             case 'TreePanel': // our new panel!
37627                 cfg.el = this.el.createChild();
37628                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37629                 this.add(region, ret);
37630                 break;
37631             */
37632             
37633             case 'Nest': 
37634                 // create a new Layout (which is  a Border Layout...
37635                 
37636                 var clayout = cfg.layout;
37637                 clayout.el  = this.el.createChild();
37638                 clayout.items   = clayout.items  || [];
37639                 
37640                 delete cfg.layout;
37641                 
37642                 // replace this exitems with the clayout ones..
37643                 xitems = clayout.items;
37644                  
37645                 // force background off if it's in center...
37646                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37647                     cfg.background = false;
37648                 }
37649                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37650                 
37651                 
37652                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37653                 //console.log('adding nested layout panel '  + cfg.toSource());
37654                 this.add(region, ret);
37655                 nb = {}; /// find first...
37656                 break;
37657             
37658             case 'Grid':
37659                 
37660                 // needs grid and region
37661                 
37662                 //var el = this.getRegion(region).el.createChild();
37663                 /*
37664                  *var el = this.el.createChild();
37665                 // create the grid first...
37666                 cfg.grid.container = el;
37667                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37668                 */
37669                 
37670                 if (region == 'center' && this.active ) {
37671                     cfg.background = false;
37672                 }
37673                 
37674                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37675                 
37676                 this.add(region, ret);
37677                 /*
37678                 if (cfg.background) {
37679                     // render grid on panel activation (if panel background)
37680                     ret.on('activate', function(gp) {
37681                         if (!gp.grid.rendered) {
37682                     //        gp.grid.render(el);
37683                         }
37684                     });
37685                 } else {
37686                   //  cfg.grid.render(el);
37687                 }
37688                 */
37689                 break;
37690            
37691            
37692             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37693                 // it was the old xcomponent building that caused this before.
37694                 // espeically if border is the top element in the tree.
37695                 ret = this;
37696                 break; 
37697                 
37698                     
37699                 
37700                 
37701                 
37702             default:
37703                 /*
37704                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37705                     
37706                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37707                     this.add(region, ret);
37708                 } else {
37709                 */
37710                     Roo.log(cfg);
37711                     throw "Can not add '" + cfg.xtype + "' to Border";
37712                     return null;
37713              
37714                                 
37715              
37716         }
37717         this.beginUpdate();
37718         // add children..
37719         var region = '';
37720         var abn = {};
37721         Roo.each(xitems, function(i)  {
37722             region = nb && i.region ? i.region : false;
37723             
37724             var add = ret.addxtype(i);
37725            
37726             if (region) {
37727                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37728                 if (!i.background) {
37729                     abn[region] = nb[region] ;
37730                 }
37731             }
37732             
37733         });
37734         this.endUpdate();
37735
37736         // make the last non-background panel active..
37737         //if (nb) { Roo.log(abn); }
37738         if (nb) {
37739             
37740             for(var r in abn) {
37741                 region = this.getRegion(r);
37742                 if (region) {
37743                     // tried using nb[r], but it does not work..
37744                      
37745                     region.showPanel(abn[r]);
37746                    
37747                 }
37748             }
37749         }
37750         return ret;
37751         
37752     },
37753     
37754     
37755 // private
37756     factory : function(cfg)
37757     {
37758         
37759         var validRegions = Roo.bootstrap.layout.Border.regions;
37760
37761         var target = cfg.region;
37762         cfg.mgr = this;
37763         
37764         var r = Roo.bootstrap.layout;
37765         Roo.log(target);
37766         switch(target){
37767             case "north":
37768                 return new r.North(cfg);
37769             case "south":
37770                 return new r.South(cfg);
37771             case "east":
37772                 return new r.East(cfg);
37773             case "west":
37774                 return new r.West(cfg);
37775             case "center":
37776                 return new r.Center(cfg);
37777         }
37778         throw 'Layout region "'+target+'" not supported.';
37779     }
37780     
37781     
37782 });
37783  /*
37784  * Based on:
37785  * Ext JS Library 1.1.1
37786  * Copyright(c) 2006-2007, Ext JS, LLC.
37787  *
37788  * Originally Released Under LGPL - original licence link has changed is not relivant.
37789  *
37790  * Fork - LGPL
37791  * <script type="text/javascript">
37792  */
37793  
37794 /**
37795  * @class Roo.bootstrap.layout.Basic
37796  * @extends Roo.util.Observable
37797  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37798  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37799  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37800  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37801  * @cfg {string}   region  the region that it inhabits..
37802  * @cfg {bool}   skipConfig skip config?
37803  * 
37804
37805  */
37806 Roo.bootstrap.layout.Basic = function(config){
37807     
37808     this.mgr = config.mgr;
37809     
37810     this.position = config.region;
37811     
37812     var skipConfig = config.skipConfig;
37813     
37814     this.events = {
37815         /**
37816          * @scope Roo.BasicLayoutRegion
37817          */
37818         
37819         /**
37820          * @event beforeremove
37821          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37822          * @param {Roo.LayoutRegion} this
37823          * @param {Roo.ContentPanel} panel The panel
37824          * @param {Object} e The cancel event object
37825          */
37826         "beforeremove" : true,
37827         /**
37828          * @event invalidated
37829          * Fires when the layout for this region is changed.
37830          * @param {Roo.LayoutRegion} this
37831          */
37832         "invalidated" : true,
37833         /**
37834          * @event visibilitychange
37835          * Fires when this region is shown or hidden 
37836          * @param {Roo.LayoutRegion} this
37837          * @param {Boolean} visibility true or false
37838          */
37839         "visibilitychange" : true,
37840         /**
37841          * @event paneladded
37842          * Fires when a panel is added. 
37843          * @param {Roo.LayoutRegion} this
37844          * @param {Roo.ContentPanel} panel The panel
37845          */
37846         "paneladded" : true,
37847         /**
37848          * @event panelremoved
37849          * Fires when a panel is removed. 
37850          * @param {Roo.LayoutRegion} this
37851          * @param {Roo.ContentPanel} panel The panel
37852          */
37853         "panelremoved" : true,
37854         /**
37855          * @event beforecollapse
37856          * Fires when this region before collapse.
37857          * @param {Roo.LayoutRegion} this
37858          */
37859         "beforecollapse" : true,
37860         /**
37861          * @event collapsed
37862          * Fires when this region is collapsed.
37863          * @param {Roo.LayoutRegion} this
37864          */
37865         "collapsed" : true,
37866         /**
37867          * @event expanded
37868          * Fires when this region is expanded.
37869          * @param {Roo.LayoutRegion} this
37870          */
37871         "expanded" : true,
37872         /**
37873          * @event slideshow
37874          * Fires when this region is slid into view.
37875          * @param {Roo.LayoutRegion} this
37876          */
37877         "slideshow" : true,
37878         /**
37879          * @event slidehide
37880          * Fires when this region slides out of view. 
37881          * @param {Roo.LayoutRegion} this
37882          */
37883         "slidehide" : true,
37884         /**
37885          * @event panelactivated
37886          * Fires when a panel is activated. 
37887          * @param {Roo.LayoutRegion} this
37888          * @param {Roo.ContentPanel} panel The activated panel
37889          */
37890         "panelactivated" : true,
37891         /**
37892          * @event resized
37893          * Fires when the user resizes this region. 
37894          * @param {Roo.LayoutRegion} this
37895          * @param {Number} newSize The new size (width for east/west, height for north/south)
37896          */
37897         "resized" : true
37898     };
37899     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37900     this.panels = new Roo.util.MixedCollection();
37901     this.panels.getKey = this.getPanelId.createDelegate(this);
37902     this.box = null;
37903     this.activePanel = null;
37904     // ensure listeners are added...
37905     
37906     if (config.listeners || config.events) {
37907         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37908             listeners : config.listeners || {},
37909             events : config.events || {}
37910         });
37911     }
37912     
37913     if(skipConfig !== true){
37914         this.applyConfig(config);
37915     }
37916 };
37917
37918 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37919 {
37920     getPanelId : function(p){
37921         return p.getId();
37922     },
37923     
37924     applyConfig : function(config){
37925         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37926         this.config = config;
37927         
37928     },
37929     
37930     /**
37931      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37932      * the width, for horizontal (north, south) the height.
37933      * @param {Number} newSize The new width or height
37934      */
37935     resizeTo : function(newSize){
37936         var el = this.el ? this.el :
37937                  (this.activePanel ? this.activePanel.getEl() : null);
37938         if(el){
37939             switch(this.position){
37940                 case "east":
37941                 case "west":
37942                     el.setWidth(newSize);
37943                     this.fireEvent("resized", this, newSize);
37944                 break;
37945                 case "north":
37946                 case "south":
37947                     el.setHeight(newSize);
37948                     this.fireEvent("resized", this, newSize);
37949                 break;                
37950             }
37951         }
37952     },
37953     
37954     getBox : function(){
37955         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37956     },
37957     
37958     getMargins : function(){
37959         return this.margins;
37960     },
37961     
37962     updateBox : function(box){
37963         this.box = box;
37964         var el = this.activePanel.getEl();
37965         el.dom.style.left = box.x + "px";
37966         el.dom.style.top = box.y + "px";
37967         this.activePanel.setSize(box.width, box.height);
37968     },
37969     
37970     /**
37971      * Returns the container element for this region.
37972      * @return {Roo.Element}
37973      */
37974     getEl : function(){
37975         return this.activePanel;
37976     },
37977     
37978     /**
37979      * Returns true if this region is currently visible.
37980      * @return {Boolean}
37981      */
37982     isVisible : function(){
37983         return this.activePanel ? true : false;
37984     },
37985     
37986     setActivePanel : function(panel){
37987         panel = this.getPanel(panel);
37988         if(this.activePanel && this.activePanel != panel){
37989             this.activePanel.setActiveState(false);
37990             this.activePanel.getEl().setLeftTop(-10000,-10000);
37991         }
37992         this.activePanel = panel;
37993         panel.setActiveState(true);
37994         if(this.box){
37995             panel.setSize(this.box.width, this.box.height);
37996         }
37997         this.fireEvent("panelactivated", this, panel);
37998         this.fireEvent("invalidated");
37999     },
38000     
38001     /**
38002      * Show the specified panel.
38003      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38004      * @return {Roo.ContentPanel} The shown panel or null
38005      */
38006     showPanel : function(panel){
38007         panel = this.getPanel(panel);
38008         if(panel){
38009             this.setActivePanel(panel);
38010         }
38011         return panel;
38012     },
38013     
38014     /**
38015      * Get the active panel for this region.
38016      * @return {Roo.ContentPanel} The active panel or null
38017      */
38018     getActivePanel : function(){
38019         return this.activePanel;
38020     },
38021     
38022     /**
38023      * Add the passed ContentPanel(s)
38024      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38025      * @return {Roo.ContentPanel} The panel added (if only one was added)
38026      */
38027     add : function(panel){
38028         if(arguments.length > 1){
38029             for(var i = 0, len = arguments.length; i < len; i++) {
38030                 this.add(arguments[i]);
38031             }
38032             return null;
38033         }
38034         if(this.hasPanel(panel)){
38035             this.showPanel(panel);
38036             return panel;
38037         }
38038         var el = panel.getEl();
38039         if(el.dom.parentNode != this.mgr.el.dom){
38040             this.mgr.el.dom.appendChild(el.dom);
38041         }
38042         if(panel.setRegion){
38043             panel.setRegion(this);
38044         }
38045         this.panels.add(panel);
38046         el.setStyle("position", "absolute");
38047         if(!panel.background){
38048             this.setActivePanel(panel);
38049             if(this.config.initialSize && this.panels.getCount()==1){
38050                 this.resizeTo(this.config.initialSize);
38051             }
38052         }
38053         this.fireEvent("paneladded", this, panel);
38054         return panel;
38055     },
38056     
38057     /**
38058      * Returns true if the panel is in this region.
38059      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38060      * @return {Boolean}
38061      */
38062     hasPanel : function(panel){
38063         if(typeof panel == "object"){ // must be panel obj
38064             panel = panel.getId();
38065         }
38066         return this.getPanel(panel) ? true : false;
38067     },
38068     
38069     /**
38070      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38071      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38072      * @param {Boolean} preservePanel Overrides the config preservePanel option
38073      * @return {Roo.ContentPanel} The panel that was removed
38074      */
38075     remove : function(panel, preservePanel){
38076         panel = this.getPanel(panel);
38077         if(!panel){
38078             return null;
38079         }
38080         var e = {};
38081         this.fireEvent("beforeremove", this, panel, e);
38082         if(e.cancel === true){
38083             return null;
38084         }
38085         var panelId = panel.getId();
38086         this.panels.removeKey(panelId);
38087         return panel;
38088     },
38089     
38090     /**
38091      * Returns the panel specified or null if it's not in this region.
38092      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38093      * @return {Roo.ContentPanel}
38094      */
38095     getPanel : function(id){
38096         if(typeof id == "object"){ // must be panel obj
38097             return id;
38098         }
38099         return this.panels.get(id);
38100     },
38101     
38102     /**
38103      * Returns this regions position (north/south/east/west/center).
38104      * @return {String} 
38105      */
38106     getPosition: function(){
38107         return this.position;    
38108     }
38109 });/*
38110  * Based on:
38111  * Ext JS Library 1.1.1
38112  * Copyright(c) 2006-2007, Ext JS, LLC.
38113  *
38114  * Originally Released Under LGPL - original licence link has changed is not relivant.
38115  *
38116  * Fork - LGPL
38117  * <script type="text/javascript">
38118  */
38119  
38120 /**
38121  * @class Roo.bootstrap.layout.Region
38122  * @extends Roo.bootstrap.layout.Basic
38123  * This class represents a region in a layout manager.
38124  
38125  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38126  * @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})
38127  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38128  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38129  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38130  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38131  * @cfg {String}    title           The title for the region (overrides panel titles)
38132  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38133  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38134  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38135  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38136  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38137  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38138  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38139  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38140  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38141  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38142
38143  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38144  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38145  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38146  * @cfg {Number}    width           For East/West panels
38147  * @cfg {Number}    height          For North/South panels
38148  * @cfg {Boolean}   split           To show the splitter
38149  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38150  * 
38151  * @cfg {string}   cls             Extra CSS classes to add to region
38152  * 
38153  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38154  * @cfg {string}   region  the region that it inhabits..
38155  *
38156
38157  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38158  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38159
38160  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38161  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38162  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38163  */
38164 Roo.bootstrap.layout.Region = function(config)
38165 {
38166     this.applyConfig(config);
38167
38168     var mgr = config.mgr;
38169     var pos = config.region;
38170     config.skipConfig = true;
38171     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38172     
38173     if (mgr.el) {
38174         this.onRender(mgr.el);   
38175     }
38176      
38177     this.visible = true;
38178     this.collapsed = false;
38179     this.unrendered_panels = [];
38180 };
38181
38182 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38183
38184     position: '', // set by wrapper (eg. north/south etc..)
38185     unrendered_panels : null,  // unrendered panels.
38186     
38187     tabPosition : false,
38188     
38189     mgr: false, // points to 'Border'
38190     
38191     
38192     createBody : function(){
38193         /** This region's body element 
38194         * @type Roo.Element */
38195         this.bodyEl = this.el.createChild({
38196                 tag: "div",
38197                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38198         });
38199     },
38200
38201     onRender: function(ctr, pos)
38202     {
38203         var dh = Roo.DomHelper;
38204         /** This region's container element 
38205         * @type Roo.Element */
38206         this.el = dh.append(ctr.dom, {
38207                 tag: "div",
38208                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38209             }, true);
38210         /** This region's title element 
38211         * @type Roo.Element */
38212     
38213         this.titleEl = dh.append(this.el.dom,  {
38214                 tag: "div",
38215                 unselectable: "on",
38216                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38217                 children:[
38218                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38219                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38220                 ]
38221             }, true);
38222         
38223         this.titleEl.enableDisplayMode();
38224         /** This region's title text element 
38225         * @type HTMLElement */
38226         this.titleTextEl = this.titleEl.dom.firstChild;
38227         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38228         /*
38229         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38230         this.closeBtn.enableDisplayMode();
38231         this.closeBtn.on("click", this.closeClicked, this);
38232         this.closeBtn.hide();
38233     */
38234         this.createBody(this.config);
38235         if(this.config.hideWhenEmpty){
38236             this.hide();
38237             this.on("paneladded", this.validateVisibility, this);
38238             this.on("panelremoved", this.validateVisibility, this);
38239         }
38240         if(this.autoScroll){
38241             this.bodyEl.setStyle("overflow", "auto");
38242         }else{
38243             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38244         }
38245         //if(c.titlebar !== false){
38246             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38247                 this.titleEl.hide();
38248             }else{
38249                 this.titleEl.show();
38250                 if(this.config.title){
38251                     this.titleTextEl.innerHTML = this.config.title;
38252                 }
38253             }
38254         //}
38255         if(this.config.collapsed){
38256             this.collapse(true);
38257         }
38258         if(this.config.hidden){
38259             this.hide();
38260         }
38261         
38262         if (this.unrendered_panels && this.unrendered_panels.length) {
38263             for (var i =0;i< this.unrendered_panels.length; i++) {
38264                 this.add(this.unrendered_panels[i]);
38265             }
38266             this.unrendered_panels = null;
38267             
38268         }
38269         
38270     },
38271     
38272     applyConfig : function(c)
38273     {
38274         /*
38275          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38276             var dh = Roo.DomHelper;
38277             if(c.titlebar !== false){
38278                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38279                 this.collapseBtn.on("click", this.collapse, this);
38280                 this.collapseBtn.enableDisplayMode();
38281                 /*
38282                 if(c.showPin === true || this.showPin){
38283                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38284                     this.stickBtn.enableDisplayMode();
38285                     this.stickBtn.on("click", this.expand, this);
38286                     this.stickBtn.hide();
38287                 }
38288                 
38289             }
38290             */
38291             /** This region's collapsed element
38292             * @type Roo.Element */
38293             /*
38294              *
38295             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38296                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38297             ]}, true);
38298             
38299             if(c.floatable !== false){
38300                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38301                this.collapsedEl.on("click", this.collapseClick, this);
38302             }
38303
38304             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38305                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38306                    id: "message", unselectable: "on", style:{"float":"left"}});
38307                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38308              }
38309             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38310             this.expandBtn.on("click", this.expand, this);
38311             
38312         }
38313         
38314         if(this.collapseBtn){
38315             this.collapseBtn.setVisible(c.collapsible == true);
38316         }
38317         
38318         this.cmargins = c.cmargins || this.cmargins ||
38319                          (this.position == "west" || this.position == "east" ?
38320                              {top: 0, left: 2, right:2, bottom: 0} :
38321                              {top: 2, left: 0, right:0, bottom: 2});
38322         */
38323         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38324         
38325         
38326         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38327         
38328         this.autoScroll = c.autoScroll || false;
38329         
38330         
38331        
38332         
38333         this.duration = c.duration || .30;
38334         this.slideDuration = c.slideDuration || .45;
38335         this.config = c;
38336        
38337     },
38338     /**
38339      * Returns true if this region is currently visible.
38340      * @return {Boolean}
38341      */
38342     isVisible : function(){
38343         return this.visible;
38344     },
38345
38346     /**
38347      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38348      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38349      */
38350     //setCollapsedTitle : function(title){
38351     //    title = title || "&#160;";
38352      //   if(this.collapsedTitleTextEl){
38353       //      this.collapsedTitleTextEl.innerHTML = title;
38354        // }
38355     //},
38356
38357     getBox : function(){
38358         var b;
38359       //  if(!this.collapsed){
38360             b = this.el.getBox(false, true);
38361        // }else{
38362           //  b = this.collapsedEl.getBox(false, true);
38363         //}
38364         return b;
38365     },
38366
38367     getMargins : function(){
38368         return this.margins;
38369         //return this.collapsed ? this.cmargins : this.margins;
38370     },
38371 /*
38372     highlight : function(){
38373         this.el.addClass("x-layout-panel-dragover");
38374     },
38375
38376     unhighlight : function(){
38377         this.el.removeClass("x-layout-panel-dragover");
38378     },
38379 */
38380     updateBox : function(box)
38381     {
38382         if (!this.bodyEl) {
38383             return; // not rendered yet..
38384         }
38385         
38386         this.box = box;
38387         if(!this.collapsed){
38388             this.el.dom.style.left = box.x + "px";
38389             this.el.dom.style.top = box.y + "px";
38390             this.updateBody(box.width, box.height);
38391         }else{
38392             this.collapsedEl.dom.style.left = box.x + "px";
38393             this.collapsedEl.dom.style.top = box.y + "px";
38394             this.collapsedEl.setSize(box.width, box.height);
38395         }
38396         if(this.tabs){
38397             this.tabs.autoSizeTabs();
38398         }
38399     },
38400
38401     updateBody : function(w, h)
38402     {
38403         if(w !== null){
38404             this.el.setWidth(w);
38405             w -= this.el.getBorderWidth("rl");
38406             if(this.config.adjustments){
38407                 w += this.config.adjustments[0];
38408             }
38409         }
38410         if(h !== null && h > 0){
38411             this.el.setHeight(h);
38412             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38413             h -= this.el.getBorderWidth("tb");
38414             if(this.config.adjustments){
38415                 h += this.config.adjustments[1];
38416             }
38417             this.bodyEl.setHeight(h);
38418             if(this.tabs){
38419                 h = this.tabs.syncHeight(h);
38420             }
38421         }
38422         if(this.panelSize){
38423             w = w !== null ? w : this.panelSize.width;
38424             h = h !== null ? h : this.panelSize.height;
38425         }
38426         if(this.activePanel){
38427             var el = this.activePanel.getEl();
38428             w = w !== null ? w : el.getWidth();
38429             h = h !== null ? h : el.getHeight();
38430             this.panelSize = {width: w, height: h};
38431             this.activePanel.setSize(w, h);
38432         }
38433         if(Roo.isIE && this.tabs){
38434             this.tabs.el.repaint();
38435         }
38436     },
38437
38438     /**
38439      * Returns the container element for this region.
38440      * @return {Roo.Element}
38441      */
38442     getEl : function(){
38443         return this.el;
38444     },
38445
38446     /**
38447      * Hides this region.
38448      */
38449     hide : function(){
38450         //if(!this.collapsed){
38451             this.el.dom.style.left = "-2000px";
38452             this.el.hide();
38453         //}else{
38454          //   this.collapsedEl.dom.style.left = "-2000px";
38455          //   this.collapsedEl.hide();
38456        // }
38457         this.visible = false;
38458         this.fireEvent("visibilitychange", this, false);
38459     },
38460
38461     /**
38462      * Shows this region if it was previously hidden.
38463      */
38464     show : function(){
38465         //if(!this.collapsed){
38466             this.el.show();
38467         //}else{
38468         //    this.collapsedEl.show();
38469        // }
38470         this.visible = true;
38471         this.fireEvent("visibilitychange", this, true);
38472     },
38473 /*
38474     closeClicked : function(){
38475         if(this.activePanel){
38476             this.remove(this.activePanel);
38477         }
38478     },
38479
38480     collapseClick : function(e){
38481         if(this.isSlid){
38482            e.stopPropagation();
38483            this.slideIn();
38484         }else{
38485            e.stopPropagation();
38486            this.slideOut();
38487         }
38488     },
38489 */
38490     /**
38491      * Collapses this region.
38492      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38493      */
38494     /*
38495     collapse : function(skipAnim, skipCheck = false){
38496         if(this.collapsed) {
38497             return;
38498         }
38499         
38500         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38501             
38502             this.collapsed = true;
38503             if(this.split){
38504                 this.split.el.hide();
38505             }
38506             if(this.config.animate && skipAnim !== true){
38507                 this.fireEvent("invalidated", this);
38508                 this.animateCollapse();
38509             }else{
38510                 this.el.setLocation(-20000,-20000);
38511                 this.el.hide();
38512                 this.collapsedEl.show();
38513                 this.fireEvent("collapsed", this);
38514                 this.fireEvent("invalidated", this);
38515             }
38516         }
38517         
38518     },
38519 */
38520     animateCollapse : function(){
38521         // overridden
38522     },
38523
38524     /**
38525      * Expands this region if it was previously collapsed.
38526      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38527      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38528      */
38529     /*
38530     expand : function(e, skipAnim){
38531         if(e) {
38532             e.stopPropagation();
38533         }
38534         if(!this.collapsed || this.el.hasActiveFx()) {
38535             return;
38536         }
38537         if(this.isSlid){
38538             this.afterSlideIn();
38539             skipAnim = true;
38540         }
38541         this.collapsed = false;
38542         if(this.config.animate && skipAnim !== true){
38543             this.animateExpand();
38544         }else{
38545             this.el.show();
38546             if(this.split){
38547                 this.split.el.show();
38548             }
38549             this.collapsedEl.setLocation(-2000,-2000);
38550             this.collapsedEl.hide();
38551             this.fireEvent("invalidated", this);
38552             this.fireEvent("expanded", this);
38553         }
38554     },
38555 */
38556     animateExpand : function(){
38557         // overridden
38558     },
38559
38560     initTabs : function()
38561     {
38562         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38563         
38564         var ts = new Roo.bootstrap.panel.Tabs({
38565             el: this.bodyEl.dom,
38566             region : this,
38567             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38568             disableTooltips: this.config.disableTabTips,
38569             toolbar : this.config.toolbar
38570         });
38571         
38572         if(this.config.hideTabs){
38573             ts.stripWrap.setDisplayed(false);
38574         }
38575         this.tabs = ts;
38576         ts.resizeTabs = this.config.resizeTabs === true;
38577         ts.minTabWidth = this.config.minTabWidth || 40;
38578         ts.maxTabWidth = this.config.maxTabWidth || 250;
38579         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38580         ts.monitorResize = false;
38581         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38582         ts.bodyEl.addClass('roo-layout-tabs-body');
38583         this.panels.each(this.initPanelAsTab, this);
38584     },
38585
38586     initPanelAsTab : function(panel){
38587         var ti = this.tabs.addTab(
38588             panel.getEl().id,
38589             panel.getTitle(),
38590             null,
38591             this.config.closeOnTab && panel.isClosable(),
38592             panel.tpl
38593         );
38594         if(panel.tabTip !== undefined){
38595             ti.setTooltip(panel.tabTip);
38596         }
38597         ti.on("activate", function(){
38598               this.setActivePanel(panel);
38599         }, this);
38600         
38601         if(this.config.closeOnTab){
38602             ti.on("beforeclose", function(t, e){
38603                 e.cancel = true;
38604                 this.remove(panel);
38605             }, this);
38606         }
38607         
38608         panel.tabItem = ti;
38609         
38610         return ti;
38611     },
38612
38613     updatePanelTitle : function(panel, title)
38614     {
38615         if(this.activePanel == panel){
38616             this.updateTitle(title);
38617         }
38618         if(this.tabs){
38619             var ti = this.tabs.getTab(panel.getEl().id);
38620             ti.setText(title);
38621             if(panel.tabTip !== undefined){
38622                 ti.setTooltip(panel.tabTip);
38623             }
38624         }
38625     },
38626
38627     updateTitle : function(title){
38628         if(this.titleTextEl && !this.config.title){
38629             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38630         }
38631     },
38632
38633     setActivePanel : function(panel)
38634     {
38635         panel = this.getPanel(panel);
38636         if(this.activePanel && this.activePanel != panel){
38637             if(this.activePanel.setActiveState(false) === false){
38638                 return;
38639             }
38640         }
38641         this.activePanel = panel;
38642         panel.setActiveState(true);
38643         if(this.panelSize){
38644             panel.setSize(this.panelSize.width, this.panelSize.height);
38645         }
38646         if(this.closeBtn){
38647             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38648         }
38649         this.updateTitle(panel.getTitle());
38650         if(this.tabs){
38651             this.fireEvent("invalidated", this);
38652         }
38653         this.fireEvent("panelactivated", this, panel);
38654     },
38655
38656     /**
38657      * Shows the specified panel.
38658      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38659      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38660      */
38661     showPanel : function(panel)
38662     {
38663         panel = this.getPanel(panel);
38664         if(panel){
38665             if(this.tabs){
38666                 var tab = this.tabs.getTab(panel.getEl().id);
38667                 if(tab.isHidden()){
38668                     this.tabs.unhideTab(tab.id);
38669                 }
38670                 tab.activate();
38671             }else{
38672                 this.setActivePanel(panel);
38673             }
38674         }
38675         return panel;
38676     },
38677
38678     /**
38679      * Get the active panel for this region.
38680      * @return {Roo.ContentPanel} The active panel or null
38681      */
38682     getActivePanel : function(){
38683         return this.activePanel;
38684     },
38685
38686     validateVisibility : function(){
38687         if(this.panels.getCount() < 1){
38688             this.updateTitle("&#160;");
38689             this.closeBtn.hide();
38690             this.hide();
38691         }else{
38692             if(!this.isVisible()){
38693                 this.show();
38694             }
38695         }
38696     },
38697
38698     /**
38699      * Adds the passed ContentPanel(s) to this region.
38700      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38701      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38702      */
38703     add : function(panel)
38704     {
38705         if(arguments.length > 1){
38706             for(var i = 0, len = arguments.length; i < len; i++) {
38707                 this.add(arguments[i]);
38708             }
38709             return null;
38710         }
38711         
38712         // if we have not been rendered yet, then we can not really do much of this..
38713         if (!this.bodyEl) {
38714             this.unrendered_panels.push(panel);
38715             return panel;
38716         }
38717         
38718         
38719         
38720         
38721         if(this.hasPanel(panel)){
38722             this.showPanel(panel);
38723             return panel;
38724         }
38725         panel.setRegion(this);
38726         this.panels.add(panel);
38727        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38728             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38729             // and hide them... ???
38730             this.bodyEl.dom.appendChild(panel.getEl().dom);
38731             if(panel.background !== true){
38732                 this.setActivePanel(panel);
38733             }
38734             this.fireEvent("paneladded", this, panel);
38735             return panel;
38736         }
38737         */
38738         if(!this.tabs){
38739             this.initTabs();
38740         }else{
38741             this.initPanelAsTab(panel);
38742         }
38743         
38744         
38745         if(panel.background !== true){
38746             this.tabs.activate(panel.getEl().id);
38747         }
38748         this.fireEvent("paneladded", this, panel);
38749         return panel;
38750     },
38751
38752     /**
38753      * Hides the tab for the specified panel.
38754      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38755      */
38756     hidePanel : function(panel){
38757         if(this.tabs && (panel = this.getPanel(panel))){
38758             this.tabs.hideTab(panel.getEl().id);
38759         }
38760     },
38761
38762     /**
38763      * Unhides the tab for a previously hidden panel.
38764      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38765      */
38766     unhidePanel : function(panel){
38767         if(this.tabs && (panel = this.getPanel(panel))){
38768             this.tabs.unhideTab(panel.getEl().id);
38769         }
38770     },
38771
38772     clearPanels : function(){
38773         while(this.panels.getCount() > 0){
38774              this.remove(this.panels.first());
38775         }
38776     },
38777
38778     /**
38779      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38780      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38781      * @param {Boolean} preservePanel Overrides the config preservePanel option
38782      * @return {Roo.ContentPanel} The panel that was removed
38783      */
38784     remove : function(panel, preservePanel)
38785     {
38786         panel = this.getPanel(panel);
38787         if(!panel){
38788             return null;
38789         }
38790         var e = {};
38791         this.fireEvent("beforeremove", this, panel, e);
38792         if(e.cancel === true){
38793             return null;
38794         }
38795         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38796         var panelId = panel.getId();
38797         this.panels.removeKey(panelId);
38798         if(preservePanel){
38799             document.body.appendChild(panel.getEl().dom);
38800         }
38801         if(this.tabs){
38802             this.tabs.removeTab(panel.getEl().id);
38803         }else if (!preservePanel){
38804             this.bodyEl.dom.removeChild(panel.getEl().dom);
38805         }
38806         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38807             var p = this.panels.first();
38808             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38809             tempEl.appendChild(p.getEl().dom);
38810             this.bodyEl.update("");
38811             this.bodyEl.dom.appendChild(p.getEl().dom);
38812             tempEl = null;
38813             this.updateTitle(p.getTitle());
38814             this.tabs = null;
38815             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38816             this.setActivePanel(p);
38817         }
38818         panel.setRegion(null);
38819         if(this.activePanel == panel){
38820             this.activePanel = null;
38821         }
38822         if(this.config.autoDestroy !== false && preservePanel !== true){
38823             try{panel.destroy();}catch(e){}
38824         }
38825         this.fireEvent("panelremoved", this, panel);
38826         return panel;
38827     },
38828
38829     /**
38830      * Returns the TabPanel component used by this region
38831      * @return {Roo.TabPanel}
38832      */
38833     getTabs : function(){
38834         return this.tabs;
38835     },
38836
38837     createTool : function(parentEl, className){
38838         var btn = Roo.DomHelper.append(parentEl, {
38839             tag: "div",
38840             cls: "x-layout-tools-button",
38841             children: [ {
38842                 tag: "div",
38843                 cls: "roo-layout-tools-button-inner " + className,
38844                 html: "&#160;"
38845             }]
38846         }, true);
38847         btn.addClassOnOver("roo-layout-tools-button-over");
38848         return btn;
38849     }
38850 });/*
38851  * Based on:
38852  * Ext JS Library 1.1.1
38853  * Copyright(c) 2006-2007, Ext JS, LLC.
38854  *
38855  * Originally Released Under LGPL - original licence link has changed is not relivant.
38856  *
38857  * Fork - LGPL
38858  * <script type="text/javascript">
38859  */
38860  
38861
38862
38863 /**
38864  * @class Roo.SplitLayoutRegion
38865  * @extends Roo.LayoutRegion
38866  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38867  */
38868 Roo.bootstrap.layout.Split = function(config){
38869     this.cursor = config.cursor;
38870     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38871 };
38872
38873 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38874 {
38875     splitTip : "Drag to resize.",
38876     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38877     useSplitTips : false,
38878
38879     applyConfig : function(config){
38880         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38881     },
38882     
38883     onRender : function(ctr,pos) {
38884         
38885         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38886         if(!this.config.split){
38887             return;
38888         }
38889         if(!this.split){
38890             
38891             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38892                             tag: "div",
38893                             id: this.el.id + "-split",
38894                             cls: "roo-layout-split roo-layout-split-"+this.position,
38895                             html: "&#160;"
38896             });
38897             /** The SplitBar for this region 
38898             * @type Roo.SplitBar */
38899             // does not exist yet...
38900             Roo.log([this.position, this.orientation]);
38901             
38902             this.split = new Roo.bootstrap.SplitBar({
38903                 dragElement : splitEl,
38904                 resizingElement: this.el,
38905                 orientation : this.orientation
38906             });
38907             
38908             this.split.on("moved", this.onSplitMove, this);
38909             this.split.useShim = this.config.useShim === true;
38910             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38911             if(this.useSplitTips){
38912                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38913             }
38914             //if(config.collapsible){
38915             //    this.split.el.on("dblclick", this.collapse,  this);
38916             //}
38917         }
38918         if(typeof this.config.minSize != "undefined"){
38919             this.split.minSize = this.config.minSize;
38920         }
38921         if(typeof this.config.maxSize != "undefined"){
38922             this.split.maxSize = this.config.maxSize;
38923         }
38924         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38925             this.hideSplitter();
38926         }
38927         
38928     },
38929
38930     getHMaxSize : function(){
38931          var cmax = this.config.maxSize || 10000;
38932          var center = this.mgr.getRegion("center");
38933          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38934     },
38935
38936     getVMaxSize : function(){
38937          var cmax = this.config.maxSize || 10000;
38938          var center = this.mgr.getRegion("center");
38939          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38940     },
38941
38942     onSplitMove : function(split, newSize){
38943         this.fireEvent("resized", this, newSize);
38944     },
38945     
38946     /** 
38947      * Returns the {@link Roo.SplitBar} for this region.
38948      * @return {Roo.SplitBar}
38949      */
38950     getSplitBar : function(){
38951         return this.split;
38952     },
38953     
38954     hide : function(){
38955         this.hideSplitter();
38956         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38957     },
38958
38959     hideSplitter : function(){
38960         if(this.split){
38961             this.split.el.setLocation(-2000,-2000);
38962             this.split.el.hide();
38963         }
38964     },
38965
38966     show : function(){
38967         if(this.split){
38968             this.split.el.show();
38969         }
38970         Roo.bootstrap.layout.Split.superclass.show.call(this);
38971     },
38972     
38973     beforeSlide: function(){
38974         if(Roo.isGecko){// firefox overflow auto bug workaround
38975             this.bodyEl.clip();
38976             if(this.tabs) {
38977                 this.tabs.bodyEl.clip();
38978             }
38979             if(this.activePanel){
38980                 this.activePanel.getEl().clip();
38981                 
38982                 if(this.activePanel.beforeSlide){
38983                     this.activePanel.beforeSlide();
38984                 }
38985             }
38986         }
38987     },
38988     
38989     afterSlide : function(){
38990         if(Roo.isGecko){// firefox overflow auto bug workaround
38991             this.bodyEl.unclip();
38992             if(this.tabs) {
38993                 this.tabs.bodyEl.unclip();
38994             }
38995             if(this.activePanel){
38996                 this.activePanel.getEl().unclip();
38997                 if(this.activePanel.afterSlide){
38998                     this.activePanel.afterSlide();
38999                 }
39000             }
39001         }
39002     },
39003
39004     initAutoHide : function(){
39005         if(this.autoHide !== false){
39006             if(!this.autoHideHd){
39007                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39008                 this.autoHideHd = {
39009                     "mouseout": function(e){
39010                         if(!e.within(this.el, true)){
39011                             st.delay(500);
39012                         }
39013                     },
39014                     "mouseover" : function(e){
39015                         st.cancel();
39016                     },
39017                     scope : this
39018                 };
39019             }
39020             this.el.on(this.autoHideHd);
39021         }
39022     },
39023
39024     clearAutoHide : function(){
39025         if(this.autoHide !== false){
39026             this.el.un("mouseout", this.autoHideHd.mouseout);
39027             this.el.un("mouseover", this.autoHideHd.mouseover);
39028         }
39029     },
39030
39031     clearMonitor : function(){
39032         Roo.get(document).un("click", this.slideInIf, this);
39033     },
39034
39035     // these names are backwards but not changed for compat
39036     slideOut : function(){
39037         if(this.isSlid || this.el.hasActiveFx()){
39038             return;
39039         }
39040         this.isSlid = true;
39041         if(this.collapseBtn){
39042             this.collapseBtn.hide();
39043         }
39044         this.closeBtnState = this.closeBtn.getStyle('display');
39045         this.closeBtn.hide();
39046         if(this.stickBtn){
39047             this.stickBtn.show();
39048         }
39049         this.el.show();
39050         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39051         this.beforeSlide();
39052         this.el.setStyle("z-index", 10001);
39053         this.el.slideIn(this.getSlideAnchor(), {
39054             callback: function(){
39055                 this.afterSlide();
39056                 this.initAutoHide();
39057                 Roo.get(document).on("click", this.slideInIf, this);
39058                 this.fireEvent("slideshow", this);
39059             },
39060             scope: this,
39061             block: true
39062         });
39063     },
39064
39065     afterSlideIn : function(){
39066         this.clearAutoHide();
39067         this.isSlid = false;
39068         this.clearMonitor();
39069         this.el.setStyle("z-index", "");
39070         if(this.collapseBtn){
39071             this.collapseBtn.show();
39072         }
39073         this.closeBtn.setStyle('display', this.closeBtnState);
39074         if(this.stickBtn){
39075             this.stickBtn.hide();
39076         }
39077         this.fireEvent("slidehide", this);
39078     },
39079
39080     slideIn : function(cb){
39081         if(!this.isSlid || this.el.hasActiveFx()){
39082             Roo.callback(cb);
39083             return;
39084         }
39085         this.isSlid = false;
39086         this.beforeSlide();
39087         this.el.slideOut(this.getSlideAnchor(), {
39088             callback: function(){
39089                 this.el.setLeftTop(-10000, -10000);
39090                 this.afterSlide();
39091                 this.afterSlideIn();
39092                 Roo.callback(cb);
39093             },
39094             scope: this,
39095             block: true
39096         });
39097     },
39098     
39099     slideInIf : function(e){
39100         if(!e.within(this.el)){
39101             this.slideIn();
39102         }
39103     },
39104
39105     animateCollapse : function(){
39106         this.beforeSlide();
39107         this.el.setStyle("z-index", 20000);
39108         var anchor = this.getSlideAnchor();
39109         this.el.slideOut(anchor, {
39110             callback : function(){
39111                 this.el.setStyle("z-index", "");
39112                 this.collapsedEl.slideIn(anchor, {duration:.3});
39113                 this.afterSlide();
39114                 this.el.setLocation(-10000,-10000);
39115                 this.el.hide();
39116                 this.fireEvent("collapsed", this);
39117             },
39118             scope: this,
39119             block: true
39120         });
39121     },
39122
39123     animateExpand : function(){
39124         this.beforeSlide();
39125         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39126         this.el.setStyle("z-index", 20000);
39127         this.collapsedEl.hide({
39128             duration:.1
39129         });
39130         this.el.slideIn(this.getSlideAnchor(), {
39131             callback : function(){
39132                 this.el.setStyle("z-index", "");
39133                 this.afterSlide();
39134                 if(this.split){
39135                     this.split.el.show();
39136                 }
39137                 this.fireEvent("invalidated", this);
39138                 this.fireEvent("expanded", this);
39139             },
39140             scope: this,
39141             block: true
39142         });
39143     },
39144
39145     anchors : {
39146         "west" : "left",
39147         "east" : "right",
39148         "north" : "top",
39149         "south" : "bottom"
39150     },
39151
39152     sanchors : {
39153         "west" : "l",
39154         "east" : "r",
39155         "north" : "t",
39156         "south" : "b"
39157     },
39158
39159     canchors : {
39160         "west" : "tl-tr",
39161         "east" : "tr-tl",
39162         "north" : "tl-bl",
39163         "south" : "bl-tl"
39164     },
39165
39166     getAnchor : function(){
39167         return this.anchors[this.position];
39168     },
39169
39170     getCollapseAnchor : function(){
39171         return this.canchors[this.position];
39172     },
39173
39174     getSlideAnchor : function(){
39175         return this.sanchors[this.position];
39176     },
39177
39178     getAlignAdj : function(){
39179         var cm = this.cmargins;
39180         switch(this.position){
39181             case "west":
39182                 return [0, 0];
39183             break;
39184             case "east":
39185                 return [0, 0];
39186             break;
39187             case "north":
39188                 return [0, 0];
39189             break;
39190             case "south":
39191                 return [0, 0];
39192             break;
39193         }
39194     },
39195
39196     getExpandAdj : function(){
39197         var c = this.collapsedEl, cm = this.cmargins;
39198         switch(this.position){
39199             case "west":
39200                 return [-(cm.right+c.getWidth()+cm.left), 0];
39201             break;
39202             case "east":
39203                 return [cm.right+c.getWidth()+cm.left, 0];
39204             break;
39205             case "north":
39206                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39207             break;
39208             case "south":
39209                 return [0, cm.top+cm.bottom+c.getHeight()];
39210             break;
39211         }
39212     }
39213 });/*
39214  * Based on:
39215  * Ext JS Library 1.1.1
39216  * Copyright(c) 2006-2007, Ext JS, LLC.
39217  *
39218  * Originally Released Under LGPL - original licence link has changed is not relivant.
39219  *
39220  * Fork - LGPL
39221  * <script type="text/javascript">
39222  */
39223 /*
39224  * These classes are private internal classes
39225  */
39226 Roo.bootstrap.layout.Center = function(config){
39227     config.region = "center";
39228     Roo.bootstrap.layout.Region.call(this, config);
39229     this.visible = true;
39230     this.minWidth = config.minWidth || 20;
39231     this.minHeight = config.minHeight || 20;
39232 };
39233
39234 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39235     hide : function(){
39236         // center panel can't be hidden
39237     },
39238     
39239     show : function(){
39240         // center panel can't be hidden
39241     },
39242     
39243     getMinWidth: function(){
39244         return this.minWidth;
39245     },
39246     
39247     getMinHeight: function(){
39248         return this.minHeight;
39249     }
39250 });
39251
39252
39253
39254
39255  
39256
39257
39258
39259
39260
39261
39262 Roo.bootstrap.layout.North = function(config)
39263 {
39264     config.region = 'north';
39265     config.cursor = 'n-resize';
39266     
39267     Roo.bootstrap.layout.Split.call(this, config);
39268     
39269     
39270     if(this.split){
39271         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39272         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39273         this.split.el.addClass("roo-layout-split-v");
39274     }
39275     //var size = config.initialSize || config.height;
39276     //if(this.el && typeof size != "undefined"){
39277     //    this.el.setHeight(size);
39278     //}
39279 };
39280 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39281 {
39282     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39283      
39284      
39285     onRender : function(ctr, pos)
39286     {
39287         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39288         var size = this.config.initialSize || this.config.height;
39289         if(this.el && typeof size != "undefined"){
39290             this.el.setHeight(size);
39291         }
39292     
39293     },
39294     
39295     getBox : function(){
39296         if(this.collapsed){
39297             return this.collapsedEl.getBox();
39298         }
39299         var box = this.el.getBox();
39300         if(this.split){
39301             box.height += this.split.el.getHeight();
39302         }
39303         return box;
39304     },
39305     
39306     updateBox : function(box){
39307         if(this.split && !this.collapsed){
39308             box.height -= this.split.el.getHeight();
39309             this.split.el.setLeft(box.x);
39310             this.split.el.setTop(box.y+box.height);
39311             this.split.el.setWidth(box.width);
39312         }
39313         if(this.collapsed){
39314             this.updateBody(box.width, null);
39315         }
39316         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39317     }
39318 });
39319
39320
39321
39322
39323
39324 Roo.bootstrap.layout.South = function(config){
39325     config.region = 'south';
39326     config.cursor = 's-resize';
39327     Roo.bootstrap.layout.Split.call(this, config);
39328     if(this.split){
39329         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39330         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39331         this.split.el.addClass("roo-layout-split-v");
39332     }
39333     
39334 };
39335
39336 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39337     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39338     
39339     onRender : function(ctr, pos)
39340     {
39341         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39342         var size = this.config.initialSize || this.config.height;
39343         if(this.el && typeof size != "undefined"){
39344             this.el.setHeight(size);
39345         }
39346     
39347     },
39348     
39349     getBox : function(){
39350         if(this.collapsed){
39351             return this.collapsedEl.getBox();
39352         }
39353         var box = this.el.getBox();
39354         if(this.split){
39355             var sh = this.split.el.getHeight();
39356             box.height += sh;
39357             box.y -= sh;
39358         }
39359         return box;
39360     },
39361     
39362     updateBox : function(box){
39363         if(this.split && !this.collapsed){
39364             var sh = this.split.el.getHeight();
39365             box.height -= sh;
39366             box.y += sh;
39367             this.split.el.setLeft(box.x);
39368             this.split.el.setTop(box.y-sh);
39369             this.split.el.setWidth(box.width);
39370         }
39371         if(this.collapsed){
39372             this.updateBody(box.width, null);
39373         }
39374         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39375     }
39376 });
39377
39378 Roo.bootstrap.layout.East = function(config){
39379     config.region = "east";
39380     config.cursor = "e-resize";
39381     Roo.bootstrap.layout.Split.call(this, config);
39382     if(this.split){
39383         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39384         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39385         this.split.el.addClass("roo-layout-split-h");
39386     }
39387     
39388 };
39389 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39390     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39391     
39392     onRender : function(ctr, pos)
39393     {
39394         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39395         var size = this.config.initialSize || this.config.width;
39396         if(this.el && typeof size != "undefined"){
39397             this.el.setWidth(size);
39398         }
39399     
39400     },
39401     
39402     getBox : function(){
39403         if(this.collapsed){
39404             return this.collapsedEl.getBox();
39405         }
39406         var box = this.el.getBox();
39407         if(this.split){
39408             var sw = this.split.el.getWidth();
39409             box.width += sw;
39410             box.x -= sw;
39411         }
39412         return box;
39413     },
39414
39415     updateBox : function(box){
39416         if(this.split && !this.collapsed){
39417             var sw = this.split.el.getWidth();
39418             box.width -= sw;
39419             this.split.el.setLeft(box.x);
39420             this.split.el.setTop(box.y);
39421             this.split.el.setHeight(box.height);
39422             box.x += sw;
39423         }
39424         if(this.collapsed){
39425             this.updateBody(null, box.height);
39426         }
39427         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39428     }
39429 });
39430
39431 Roo.bootstrap.layout.West = function(config){
39432     config.region = "west";
39433     config.cursor = "w-resize";
39434     
39435     Roo.bootstrap.layout.Split.call(this, config);
39436     if(this.split){
39437         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39438         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39439         this.split.el.addClass("roo-layout-split-h");
39440     }
39441     
39442 };
39443 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39444     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39445     
39446     onRender: function(ctr, pos)
39447     {
39448         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39449         var size = this.config.initialSize || this.config.width;
39450         if(typeof size != "undefined"){
39451             this.el.setWidth(size);
39452         }
39453     },
39454     
39455     getBox : function(){
39456         if(this.collapsed){
39457             return this.collapsedEl.getBox();
39458         }
39459         var box = this.el.getBox();
39460         if (box.width == 0) {
39461             box.width = this.config.width; // kludge?
39462         }
39463         if(this.split){
39464             box.width += this.split.el.getWidth();
39465         }
39466         return box;
39467     },
39468     
39469     updateBox : function(box){
39470         if(this.split && !this.collapsed){
39471             var sw = this.split.el.getWidth();
39472             box.width -= sw;
39473             this.split.el.setLeft(box.x+box.width);
39474             this.split.el.setTop(box.y);
39475             this.split.el.setHeight(box.height);
39476         }
39477         if(this.collapsed){
39478             this.updateBody(null, box.height);
39479         }
39480         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39481     }
39482 });Roo.namespace("Roo.bootstrap.panel");/*
39483  * Based on:
39484  * Ext JS Library 1.1.1
39485  * Copyright(c) 2006-2007, Ext JS, LLC.
39486  *
39487  * Originally Released Under LGPL - original licence link has changed is not relivant.
39488  *
39489  * Fork - LGPL
39490  * <script type="text/javascript">
39491  */
39492 /**
39493  * @class Roo.ContentPanel
39494  * @extends Roo.util.Observable
39495  * A basic ContentPanel element.
39496  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39497  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39498  * @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
39499  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39500  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39501  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39502  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39503  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39504  * @cfg {String} title          The title for this panel
39505  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39506  * @cfg {String} url            Calls {@link #setUrl} with this value
39507  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39508  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39509  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39510  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39511  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39512  * @cfg {Boolean} badges render the badges
39513  * @cfg {String} cls  extra classes to use  
39514  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39515
39516  * @constructor
39517  * Create a new ContentPanel.
39518  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39519  * @param {String/Object} config A string to set only the title or a config object
39520  * @param {String} content (optional) Set the HTML content for this panel
39521  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39522  */
39523 Roo.bootstrap.panel.Content = function( config){
39524     
39525     this.tpl = config.tpl || false;
39526     
39527     var el = config.el;
39528     var content = config.content;
39529
39530     if(config.autoCreate){ // xtype is available if this is called from factory
39531         el = Roo.id();
39532     }
39533     this.el = Roo.get(el);
39534     if(!this.el && config && config.autoCreate){
39535         if(typeof config.autoCreate == "object"){
39536             if(!config.autoCreate.id){
39537                 config.autoCreate.id = config.id||el;
39538             }
39539             this.el = Roo.DomHelper.append(document.body,
39540                         config.autoCreate, true);
39541         }else{
39542             var elcfg =  {
39543                 tag: "div",
39544                 cls: (config.cls || '') +
39545                     (config.background ? ' bg-' + config.background : '') +
39546                     " roo-layout-inactive-content",
39547                 id: config.id||el
39548             };
39549             if (config.iframe) {
39550                 elcfg.cn = [
39551                     {
39552                         tag : 'iframe',
39553                         style : 'border: 0px',
39554                         src : 'about:blank'
39555                     }
39556                 ];
39557             }
39558               
39559             if (config.html) {
39560                 elcfg.html = config.html;
39561                 
39562             }
39563                         
39564             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39565             if (config.iframe) {
39566                 this.iframeEl = this.el.select('iframe',true).first();
39567             }
39568             
39569         }
39570     } 
39571     this.closable = false;
39572     this.loaded = false;
39573     this.active = false;
39574    
39575       
39576     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39577         
39578         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39579         
39580         this.wrapEl = this.el; //this.el.wrap();
39581         var ti = [];
39582         if (config.toolbar.items) {
39583             ti = config.toolbar.items ;
39584             delete config.toolbar.items ;
39585         }
39586         
39587         var nitems = [];
39588         this.toolbar.render(this.wrapEl, 'before');
39589         for(var i =0;i < ti.length;i++) {
39590           //  Roo.log(['add child', items[i]]);
39591             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39592         }
39593         this.toolbar.items = nitems;
39594         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39595         delete config.toolbar;
39596         
39597     }
39598     /*
39599     // xtype created footer. - not sure if will work as we normally have to render first..
39600     if (this.footer && !this.footer.el && this.footer.xtype) {
39601         if (!this.wrapEl) {
39602             this.wrapEl = this.el.wrap();
39603         }
39604     
39605         this.footer.container = this.wrapEl.createChild();
39606          
39607         this.footer = Roo.factory(this.footer, Roo);
39608         
39609     }
39610     */
39611     
39612      if(typeof config == "string"){
39613         this.title = config;
39614     }else{
39615         Roo.apply(this, config);
39616     }
39617     
39618     if(this.resizeEl){
39619         this.resizeEl = Roo.get(this.resizeEl, true);
39620     }else{
39621         this.resizeEl = this.el;
39622     }
39623     // handle view.xtype
39624     
39625  
39626     
39627     
39628     this.addEvents({
39629         /**
39630          * @event activate
39631          * Fires when this panel is activated. 
39632          * @param {Roo.ContentPanel} this
39633          */
39634         "activate" : true,
39635         /**
39636          * @event deactivate
39637          * Fires when this panel is activated. 
39638          * @param {Roo.ContentPanel} this
39639          */
39640         "deactivate" : true,
39641
39642         /**
39643          * @event resize
39644          * Fires when this panel is resized if fitToFrame is true.
39645          * @param {Roo.ContentPanel} this
39646          * @param {Number} width The width after any component adjustments
39647          * @param {Number} height The height after any component adjustments
39648          */
39649         "resize" : true,
39650         
39651          /**
39652          * @event render
39653          * Fires when this tab is created
39654          * @param {Roo.ContentPanel} this
39655          */
39656         "render" : true
39657         
39658         
39659         
39660     });
39661     
39662
39663     
39664     
39665     if(this.autoScroll && !this.iframe){
39666         this.resizeEl.setStyle("overflow", "auto");
39667     } else {
39668         // fix randome scrolling
39669         //this.el.on('scroll', function() {
39670         //    Roo.log('fix random scolling');
39671         //    this.scrollTo('top',0); 
39672         //});
39673     }
39674     content = content || this.content;
39675     if(content){
39676         this.setContent(content);
39677     }
39678     if(config && config.url){
39679         this.setUrl(this.url, this.params, this.loadOnce);
39680     }
39681     
39682     
39683     
39684     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39685     
39686     if (this.view && typeof(this.view.xtype) != 'undefined') {
39687         this.view.el = this.el.appendChild(document.createElement("div"));
39688         this.view = Roo.factory(this.view); 
39689         this.view.render  &&  this.view.render(false, '');  
39690     }
39691     
39692     
39693     this.fireEvent('render', this);
39694 };
39695
39696 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39697     
39698     cls : '',
39699     background : '',
39700     
39701     tabTip : '',
39702     
39703     iframe : false,
39704     iframeEl : false,
39705     
39706     setRegion : function(region){
39707         this.region = region;
39708         this.setActiveClass(region && !this.background);
39709     },
39710     
39711     
39712     setActiveClass: function(state)
39713     {
39714         if(state){
39715            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39716            this.el.setStyle('position','relative');
39717         }else{
39718            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39719            this.el.setStyle('position', 'absolute');
39720         } 
39721     },
39722     
39723     /**
39724      * Returns the toolbar for this Panel if one was configured. 
39725      * @return {Roo.Toolbar} 
39726      */
39727     getToolbar : function(){
39728         return this.toolbar;
39729     },
39730     
39731     setActiveState : function(active)
39732     {
39733         this.active = active;
39734         this.setActiveClass(active);
39735         if(!active){
39736             if(this.fireEvent("deactivate", this) === false){
39737                 return false;
39738             }
39739             return true;
39740         }
39741         this.fireEvent("activate", this);
39742         return true;
39743     },
39744     /**
39745      * Updates this panel's element (not for iframe)
39746      * @param {String} content The new content
39747      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39748     */
39749     setContent : function(content, loadScripts){
39750         if (this.iframe) {
39751             return;
39752         }
39753         
39754         this.el.update(content, loadScripts);
39755     },
39756
39757     ignoreResize : function(w, h){
39758         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39759             return true;
39760         }else{
39761             this.lastSize = {width: w, height: h};
39762             return false;
39763         }
39764     },
39765     /**
39766      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39767      * @return {Roo.UpdateManager} The UpdateManager
39768      */
39769     getUpdateManager : function(){
39770         if (this.iframe) {
39771             return false;
39772         }
39773         return this.el.getUpdateManager();
39774     },
39775      /**
39776      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39777      * Does not work with IFRAME contents
39778      * @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:
39779 <pre><code>
39780 panel.load({
39781     url: "your-url.php",
39782     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39783     callback: yourFunction,
39784     scope: yourObject, //(optional scope)
39785     discardUrl: false,
39786     nocache: false,
39787     text: "Loading...",
39788     timeout: 30,
39789     scripts: false
39790 });
39791 </code></pre>
39792      
39793      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39794      * 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.
39795      * @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}
39796      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39797      * @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.
39798      * @return {Roo.ContentPanel} this
39799      */
39800     load : function(){
39801         
39802         if (this.iframe) {
39803             return this;
39804         }
39805         
39806         var um = this.el.getUpdateManager();
39807         um.update.apply(um, arguments);
39808         return this;
39809     },
39810
39811
39812     /**
39813      * 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.
39814      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39815      * @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)
39816      * @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)
39817      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39818      */
39819     setUrl : function(url, params, loadOnce){
39820         if (this.iframe) {
39821             this.iframeEl.dom.src = url;
39822             return false;
39823         }
39824         
39825         if(this.refreshDelegate){
39826             this.removeListener("activate", this.refreshDelegate);
39827         }
39828         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39829         this.on("activate", this.refreshDelegate);
39830         return this.el.getUpdateManager();
39831     },
39832     
39833     _handleRefresh : function(url, params, loadOnce){
39834         if(!loadOnce || !this.loaded){
39835             var updater = this.el.getUpdateManager();
39836             updater.update(url, params, this._setLoaded.createDelegate(this));
39837         }
39838     },
39839     
39840     _setLoaded : function(){
39841         this.loaded = true;
39842     }, 
39843     
39844     /**
39845      * Returns this panel's id
39846      * @return {String} 
39847      */
39848     getId : function(){
39849         return this.el.id;
39850     },
39851     
39852     /** 
39853      * Returns this panel's element - used by regiosn to add.
39854      * @return {Roo.Element} 
39855      */
39856     getEl : function(){
39857         return this.wrapEl || this.el;
39858     },
39859     
39860    
39861     
39862     adjustForComponents : function(width, height)
39863     {
39864         //Roo.log('adjustForComponents ');
39865         if(this.resizeEl != this.el){
39866             width -= this.el.getFrameWidth('lr');
39867             height -= this.el.getFrameWidth('tb');
39868         }
39869         if(this.toolbar){
39870             var te = this.toolbar.getEl();
39871             te.setWidth(width);
39872             height -= te.getHeight();
39873         }
39874         if(this.footer){
39875             var te = this.footer.getEl();
39876             te.setWidth(width);
39877             height -= te.getHeight();
39878         }
39879         
39880         
39881         if(this.adjustments){
39882             width += this.adjustments[0];
39883             height += this.adjustments[1];
39884         }
39885         return {"width": width, "height": height};
39886     },
39887     
39888     setSize : function(width, height){
39889         if(this.fitToFrame && !this.ignoreResize(width, height)){
39890             if(this.fitContainer && this.resizeEl != this.el){
39891                 this.el.setSize(width, height);
39892             }
39893             var size = this.adjustForComponents(width, height);
39894             if (this.iframe) {
39895                 this.iframeEl.setSize(width,height);
39896             }
39897             
39898             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39899             this.fireEvent('resize', this, size.width, size.height);
39900             
39901             
39902         }
39903     },
39904     
39905     /**
39906      * Returns this panel's title
39907      * @return {String} 
39908      */
39909     getTitle : function(){
39910         
39911         if (typeof(this.title) != 'object') {
39912             return this.title;
39913         }
39914         
39915         var t = '';
39916         for (var k in this.title) {
39917             if (!this.title.hasOwnProperty(k)) {
39918                 continue;
39919             }
39920             
39921             if (k.indexOf('-') >= 0) {
39922                 var s = k.split('-');
39923                 for (var i = 0; i<s.length; i++) {
39924                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39925                 }
39926             } else {
39927                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39928             }
39929         }
39930         return t;
39931     },
39932     
39933     /**
39934      * Set this panel's title
39935      * @param {String} title
39936      */
39937     setTitle : function(title){
39938         this.title = title;
39939         if(this.region){
39940             this.region.updatePanelTitle(this, title);
39941         }
39942     },
39943     
39944     /**
39945      * Returns true is this panel was configured to be closable
39946      * @return {Boolean} 
39947      */
39948     isClosable : function(){
39949         return this.closable;
39950     },
39951     
39952     beforeSlide : function(){
39953         this.el.clip();
39954         this.resizeEl.clip();
39955     },
39956     
39957     afterSlide : function(){
39958         this.el.unclip();
39959         this.resizeEl.unclip();
39960     },
39961     
39962     /**
39963      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39964      *   Will fail silently if the {@link #setUrl} method has not been called.
39965      *   This does not activate the panel, just updates its content.
39966      */
39967     refresh : function(){
39968         if(this.refreshDelegate){
39969            this.loaded = false;
39970            this.refreshDelegate();
39971         }
39972     },
39973     
39974     /**
39975      * Destroys this panel
39976      */
39977     destroy : function(){
39978         this.el.removeAllListeners();
39979         var tempEl = document.createElement("span");
39980         tempEl.appendChild(this.el.dom);
39981         tempEl.innerHTML = "";
39982         this.el.remove();
39983         this.el = null;
39984     },
39985     
39986     /**
39987      * form - if the content panel contains a form - this is a reference to it.
39988      * @type {Roo.form.Form}
39989      */
39990     form : false,
39991     /**
39992      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39993      *    This contains a reference to it.
39994      * @type {Roo.View}
39995      */
39996     view : false,
39997     
39998       /**
39999      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40000      * <pre><code>
40001
40002 layout.addxtype({
40003        xtype : 'Form',
40004        items: [ .... ]
40005    }
40006 );
40007
40008 </code></pre>
40009      * @param {Object} cfg Xtype definition of item to add.
40010      */
40011     
40012     
40013     getChildContainer: function () {
40014         return this.getEl();
40015     }
40016     
40017     
40018     /*
40019         var  ret = new Roo.factory(cfg);
40020         return ret;
40021         
40022         
40023         // add form..
40024         if (cfg.xtype.match(/^Form$/)) {
40025             
40026             var el;
40027             //if (this.footer) {
40028             //    el = this.footer.container.insertSibling(false, 'before');
40029             //} else {
40030                 el = this.el.createChild();
40031             //}
40032
40033             this.form = new  Roo.form.Form(cfg);
40034             
40035             
40036             if ( this.form.allItems.length) {
40037                 this.form.render(el.dom);
40038             }
40039             return this.form;
40040         }
40041         // should only have one of theses..
40042         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40043             // views.. should not be just added - used named prop 'view''
40044             
40045             cfg.el = this.el.appendChild(document.createElement("div"));
40046             // factory?
40047             
40048             var ret = new Roo.factory(cfg);
40049              
40050              ret.render && ret.render(false, ''); // render blank..
40051             this.view = ret;
40052             return ret;
40053         }
40054         return false;
40055     }
40056     \*/
40057 });
40058  
40059 /**
40060  * @class Roo.bootstrap.panel.Grid
40061  * @extends Roo.bootstrap.panel.Content
40062  * @constructor
40063  * Create a new GridPanel.
40064  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40065  * @param {Object} config A the config object
40066   
40067  */
40068
40069
40070
40071 Roo.bootstrap.panel.Grid = function(config)
40072 {
40073     
40074       
40075     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40076         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40077
40078     config.el = this.wrapper;
40079     //this.el = this.wrapper;
40080     
40081       if (config.container) {
40082         // ctor'ed from a Border/panel.grid
40083         
40084         
40085         this.wrapper.setStyle("overflow", "hidden");
40086         this.wrapper.addClass('roo-grid-container');
40087
40088     }
40089     
40090     
40091     if(config.toolbar){
40092         var tool_el = this.wrapper.createChild();    
40093         this.toolbar = Roo.factory(config.toolbar);
40094         var ti = [];
40095         if (config.toolbar.items) {
40096             ti = config.toolbar.items ;
40097             delete config.toolbar.items ;
40098         }
40099         
40100         var nitems = [];
40101         this.toolbar.render(tool_el);
40102         for(var i =0;i < ti.length;i++) {
40103           //  Roo.log(['add child', items[i]]);
40104             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40105         }
40106         this.toolbar.items = nitems;
40107         
40108         delete config.toolbar;
40109     }
40110     
40111     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40112     config.grid.scrollBody = true;;
40113     config.grid.monitorWindowResize = false; // turn off autosizing
40114     config.grid.autoHeight = false;
40115     config.grid.autoWidth = false;
40116     
40117     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40118     
40119     if (config.background) {
40120         // render grid on panel activation (if panel background)
40121         this.on('activate', function(gp) {
40122             if (!gp.grid.rendered) {
40123                 gp.grid.render(this.wrapper);
40124                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40125             }
40126         });
40127             
40128     } else {
40129         this.grid.render(this.wrapper);
40130         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40131
40132     }
40133     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40134     // ??? needed ??? config.el = this.wrapper;
40135     
40136     
40137     
40138   
40139     // xtype created footer. - not sure if will work as we normally have to render first..
40140     if (this.footer && !this.footer.el && this.footer.xtype) {
40141         
40142         var ctr = this.grid.getView().getFooterPanel(true);
40143         this.footer.dataSource = this.grid.dataSource;
40144         this.footer = Roo.factory(this.footer, Roo);
40145         this.footer.render(ctr);
40146         
40147     }
40148     
40149     
40150     
40151     
40152      
40153 };
40154
40155 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40156     getId : function(){
40157         return this.grid.id;
40158     },
40159     
40160     /**
40161      * Returns the grid for this panel
40162      * @return {Roo.bootstrap.Table} 
40163      */
40164     getGrid : function(){
40165         return this.grid;    
40166     },
40167     
40168     setSize : function(width, height){
40169         if(!this.ignoreResize(width, height)){
40170             var grid = this.grid;
40171             var size = this.adjustForComponents(width, height);
40172             // tfoot is not a footer?
40173           
40174             
40175             var gridel = grid.getGridEl();
40176             gridel.setSize(size.width, size.height);
40177             
40178             var tbd = grid.getGridEl().select('tbody', true).first();
40179             var thd = grid.getGridEl().select('thead',true).first();
40180             var tbf= grid.getGridEl().select('tfoot', true).first();
40181
40182             if (tbf) {
40183                 size.height -= tbf.getHeight();
40184             }
40185             if (thd) {
40186                 size.height -= thd.getHeight();
40187             }
40188             
40189             tbd.setSize(size.width, size.height );
40190             // this is for the account management tab -seems to work there.
40191             var thd = grid.getGridEl().select('thead',true).first();
40192             //if (tbd) {
40193             //    tbd.setSize(size.width, size.height - thd.getHeight());
40194             //}
40195              
40196             grid.autoSize();
40197         }
40198     },
40199      
40200     
40201     
40202     beforeSlide : function(){
40203         this.grid.getView().scroller.clip();
40204     },
40205     
40206     afterSlide : function(){
40207         this.grid.getView().scroller.unclip();
40208     },
40209     
40210     destroy : function(){
40211         this.grid.destroy();
40212         delete this.grid;
40213         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40214     }
40215 });
40216
40217 /**
40218  * @class Roo.bootstrap.panel.Nest
40219  * @extends Roo.bootstrap.panel.Content
40220  * @constructor
40221  * Create a new Panel, that can contain a layout.Border.
40222  * 
40223  * 
40224  * @param {Roo.BorderLayout} layout The layout for this panel
40225  * @param {String/Object} config A string to set only the title or a config object
40226  */
40227 Roo.bootstrap.panel.Nest = function(config)
40228 {
40229     // construct with only one argument..
40230     /* FIXME - implement nicer consturctors
40231     if (layout.layout) {
40232         config = layout;
40233         layout = config.layout;
40234         delete config.layout;
40235     }
40236     if (layout.xtype && !layout.getEl) {
40237         // then layout needs constructing..
40238         layout = Roo.factory(layout, Roo);
40239     }
40240     */
40241     
40242     config.el =  config.layout.getEl();
40243     
40244     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40245     
40246     config.layout.monitorWindowResize = false; // turn off autosizing
40247     this.layout = config.layout;
40248     this.layout.getEl().addClass("roo-layout-nested-layout");
40249     this.layout.parent = this;
40250     
40251     
40252     
40253     
40254 };
40255
40256 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40257
40258     setSize : function(width, height){
40259         if(!this.ignoreResize(width, height)){
40260             var size = this.adjustForComponents(width, height);
40261             var el = this.layout.getEl();
40262             if (size.height < 1) {
40263                 el.setWidth(size.width);   
40264             } else {
40265                 el.setSize(size.width, size.height);
40266             }
40267             var touch = el.dom.offsetWidth;
40268             this.layout.layout();
40269             // ie requires a double layout on the first pass
40270             if(Roo.isIE && !this.initialized){
40271                 this.initialized = true;
40272                 this.layout.layout();
40273             }
40274         }
40275     },
40276     
40277     // activate all subpanels if not currently active..
40278     
40279     setActiveState : function(active){
40280         this.active = active;
40281         this.setActiveClass(active);
40282         
40283         if(!active){
40284             this.fireEvent("deactivate", this);
40285             return;
40286         }
40287         
40288         this.fireEvent("activate", this);
40289         // not sure if this should happen before or after..
40290         if (!this.layout) {
40291             return; // should not happen..
40292         }
40293         var reg = false;
40294         for (var r in this.layout.regions) {
40295             reg = this.layout.getRegion(r);
40296             if (reg.getActivePanel()) {
40297                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40298                 reg.setActivePanel(reg.getActivePanel());
40299                 continue;
40300             }
40301             if (!reg.panels.length) {
40302                 continue;
40303             }
40304             reg.showPanel(reg.getPanel(0));
40305         }
40306         
40307         
40308         
40309         
40310     },
40311     
40312     /**
40313      * Returns the nested BorderLayout for this panel
40314      * @return {Roo.BorderLayout} 
40315      */
40316     getLayout : function(){
40317         return this.layout;
40318     },
40319     
40320      /**
40321      * Adds a xtype elements to the layout of the nested panel
40322      * <pre><code>
40323
40324 panel.addxtype({
40325        xtype : 'ContentPanel',
40326        region: 'west',
40327        items: [ .... ]
40328    }
40329 );
40330
40331 panel.addxtype({
40332         xtype : 'NestedLayoutPanel',
40333         region: 'west',
40334         layout: {
40335            center: { },
40336            west: { }   
40337         },
40338         items : [ ... list of content panels or nested layout panels.. ]
40339    }
40340 );
40341 </code></pre>
40342      * @param {Object} cfg Xtype definition of item to add.
40343      */
40344     addxtype : function(cfg) {
40345         return this.layout.addxtype(cfg);
40346     
40347     }
40348 });/*
40349  * Based on:
40350  * Ext JS Library 1.1.1
40351  * Copyright(c) 2006-2007, Ext JS, LLC.
40352  *
40353  * Originally Released Under LGPL - original licence link has changed is not relivant.
40354  *
40355  * Fork - LGPL
40356  * <script type="text/javascript">
40357  */
40358 /**
40359  * @class Roo.TabPanel
40360  * @extends Roo.util.Observable
40361  * A lightweight tab container.
40362  * <br><br>
40363  * Usage:
40364  * <pre><code>
40365 // basic tabs 1, built from existing content
40366 var tabs = new Roo.TabPanel("tabs1");
40367 tabs.addTab("script", "View Script");
40368 tabs.addTab("markup", "View Markup");
40369 tabs.activate("script");
40370
40371 // more advanced tabs, built from javascript
40372 var jtabs = new Roo.TabPanel("jtabs");
40373 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40374
40375 // set up the UpdateManager
40376 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40377 var updater = tab2.getUpdateManager();
40378 updater.setDefaultUrl("ajax1.htm");
40379 tab2.on('activate', updater.refresh, updater, true);
40380
40381 // Use setUrl for Ajax loading
40382 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40383 tab3.setUrl("ajax2.htm", null, true);
40384
40385 // Disabled tab
40386 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40387 tab4.disable();
40388
40389 jtabs.activate("jtabs-1");
40390  * </code></pre>
40391  * @constructor
40392  * Create a new TabPanel.
40393  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40394  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40395  */
40396 Roo.bootstrap.panel.Tabs = function(config){
40397     /**
40398     * The container element for this TabPanel.
40399     * @type Roo.Element
40400     */
40401     this.el = Roo.get(config.el);
40402     delete config.el;
40403     if(config){
40404         if(typeof config == "boolean"){
40405             this.tabPosition = config ? "bottom" : "top";
40406         }else{
40407             Roo.apply(this, config);
40408         }
40409     }
40410     
40411     if(this.tabPosition == "bottom"){
40412         // if tabs are at the bottom = create the body first.
40413         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40414         this.el.addClass("roo-tabs-bottom");
40415     }
40416     // next create the tabs holders
40417     
40418     if (this.tabPosition == "west"){
40419         
40420         var reg = this.region; // fake it..
40421         while (reg) {
40422             if (!reg.mgr.parent) {
40423                 break;
40424             }
40425             reg = reg.mgr.parent.region;
40426         }
40427         Roo.log("got nest?");
40428         Roo.log(reg);
40429         if (reg.mgr.getRegion('west')) {
40430             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40431             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40432             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40433             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40434             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40435         
40436             
40437         }
40438         
40439         
40440     } else {
40441      
40442         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40443         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40444         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40445         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40446     }
40447     
40448     
40449     if(Roo.isIE){
40450         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40451     }
40452     
40453     // finally - if tabs are at the top, then create the body last..
40454     if(this.tabPosition != "bottom"){
40455         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40456          * @type Roo.Element
40457          */
40458         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40459         this.el.addClass("roo-tabs-top");
40460     }
40461     this.items = [];
40462
40463     this.bodyEl.setStyle("position", "relative");
40464
40465     this.active = null;
40466     this.activateDelegate = this.activate.createDelegate(this);
40467
40468     this.addEvents({
40469         /**
40470          * @event tabchange
40471          * Fires when the active tab changes
40472          * @param {Roo.TabPanel} this
40473          * @param {Roo.TabPanelItem} activePanel The new active tab
40474          */
40475         "tabchange": true,
40476         /**
40477          * @event beforetabchange
40478          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40479          * @param {Roo.TabPanel} this
40480          * @param {Object} e Set cancel to true on this object to cancel the tab change
40481          * @param {Roo.TabPanelItem} tab The tab being changed to
40482          */
40483         "beforetabchange" : true
40484     });
40485
40486     Roo.EventManager.onWindowResize(this.onResize, this);
40487     this.cpad = this.el.getPadding("lr");
40488     this.hiddenCount = 0;
40489
40490
40491     // toolbar on the tabbar support...
40492     if (this.toolbar) {
40493         alert("no toolbar support yet");
40494         this.toolbar  = false;
40495         /*
40496         var tcfg = this.toolbar;
40497         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40498         this.toolbar = new Roo.Toolbar(tcfg);
40499         if (Roo.isSafari) {
40500             var tbl = tcfg.container.child('table', true);
40501             tbl.setAttribute('width', '100%');
40502         }
40503         */
40504         
40505     }
40506    
40507
40508
40509     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40510 };
40511
40512 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40513     /*
40514      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40515      */
40516     tabPosition : "top",
40517     /*
40518      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40519      */
40520     currentTabWidth : 0,
40521     /*
40522      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40523      */
40524     minTabWidth : 40,
40525     /*
40526      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40527      */
40528     maxTabWidth : 250,
40529     /*
40530      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40531      */
40532     preferredTabWidth : 175,
40533     /*
40534      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40535      */
40536     resizeTabs : false,
40537     /*
40538      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40539      */
40540     monitorResize : true,
40541     /*
40542      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40543      */
40544     toolbar : false,  // set by caller..
40545     
40546     region : false, /// set by caller
40547     
40548     disableTooltips : true, // not used yet...
40549
40550     /**
40551      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40552      * @param {String} id The id of the div to use <b>or create</b>
40553      * @param {String} text The text for the tab
40554      * @param {String} content (optional) Content to put in the TabPanelItem body
40555      * @param {Boolean} closable (optional) True to create a close icon on the tab
40556      * @return {Roo.TabPanelItem} The created TabPanelItem
40557      */
40558     addTab : function(id, text, content, closable, tpl)
40559     {
40560         var item = new Roo.bootstrap.panel.TabItem({
40561             panel: this,
40562             id : id,
40563             text : text,
40564             closable : closable,
40565             tpl : tpl
40566         });
40567         this.addTabItem(item);
40568         if(content){
40569             item.setContent(content);
40570         }
40571         return item;
40572     },
40573
40574     /**
40575      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40576      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40577      * @return {Roo.TabPanelItem}
40578      */
40579     getTab : function(id){
40580         return this.items[id];
40581     },
40582
40583     /**
40584      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40585      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40586      */
40587     hideTab : function(id){
40588         var t = this.items[id];
40589         if(!t.isHidden()){
40590            t.setHidden(true);
40591            this.hiddenCount++;
40592            this.autoSizeTabs();
40593         }
40594     },
40595
40596     /**
40597      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40598      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40599      */
40600     unhideTab : function(id){
40601         var t = this.items[id];
40602         if(t.isHidden()){
40603            t.setHidden(false);
40604            this.hiddenCount--;
40605            this.autoSizeTabs();
40606         }
40607     },
40608
40609     /**
40610      * Adds an existing {@link Roo.TabPanelItem}.
40611      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40612      */
40613     addTabItem : function(item)
40614     {
40615         this.items[item.id] = item;
40616         this.items.push(item);
40617         this.autoSizeTabs();
40618       //  if(this.resizeTabs){
40619     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40620   //         this.autoSizeTabs();
40621 //        }else{
40622 //            item.autoSize();
40623        // }
40624     },
40625
40626     /**
40627      * Removes a {@link Roo.TabPanelItem}.
40628      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40629      */
40630     removeTab : function(id){
40631         var items = this.items;
40632         var tab = items[id];
40633         if(!tab) { return; }
40634         var index = items.indexOf(tab);
40635         if(this.active == tab && items.length > 1){
40636             var newTab = this.getNextAvailable(index);
40637             if(newTab) {
40638                 newTab.activate();
40639             }
40640         }
40641         this.stripEl.dom.removeChild(tab.pnode.dom);
40642         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40643             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40644         }
40645         items.splice(index, 1);
40646         delete this.items[tab.id];
40647         tab.fireEvent("close", tab);
40648         tab.purgeListeners();
40649         this.autoSizeTabs();
40650     },
40651
40652     getNextAvailable : function(start){
40653         var items = this.items;
40654         var index = start;
40655         // look for a next tab that will slide over to
40656         // replace the one being removed
40657         while(index < items.length){
40658             var item = items[++index];
40659             if(item && !item.isHidden()){
40660                 return item;
40661             }
40662         }
40663         // if one isn't found select the previous tab (on the left)
40664         index = start;
40665         while(index >= 0){
40666             var item = items[--index];
40667             if(item && !item.isHidden()){
40668                 return item;
40669             }
40670         }
40671         return null;
40672     },
40673
40674     /**
40675      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40676      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40677      */
40678     disableTab : function(id){
40679         var tab = this.items[id];
40680         if(tab && this.active != tab){
40681             tab.disable();
40682         }
40683     },
40684
40685     /**
40686      * Enables a {@link Roo.TabPanelItem} that is disabled.
40687      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40688      */
40689     enableTab : function(id){
40690         var tab = this.items[id];
40691         tab.enable();
40692     },
40693
40694     /**
40695      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40696      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40697      * @return {Roo.TabPanelItem} The TabPanelItem.
40698      */
40699     activate : function(id)
40700     {
40701         //Roo.log('activite:'  + id);
40702         
40703         var tab = this.items[id];
40704         if(!tab){
40705             return null;
40706         }
40707         if(tab == this.active || tab.disabled){
40708             return tab;
40709         }
40710         var e = {};
40711         this.fireEvent("beforetabchange", this, e, tab);
40712         if(e.cancel !== true && !tab.disabled){
40713             if(this.active){
40714                 this.active.hide();
40715             }
40716             this.active = this.items[id];
40717             this.active.show();
40718             this.fireEvent("tabchange", this, this.active);
40719         }
40720         return tab;
40721     },
40722
40723     /**
40724      * Gets the active {@link Roo.TabPanelItem}.
40725      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40726      */
40727     getActiveTab : function(){
40728         return this.active;
40729     },
40730
40731     /**
40732      * Updates the tab body element to fit the height of the container element
40733      * for overflow scrolling
40734      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40735      */
40736     syncHeight : function(targetHeight){
40737         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40738         var bm = this.bodyEl.getMargins();
40739         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40740         this.bodyEl.setHeight(newHeight);
40741         return newHeight;
40742     },
40743
40744     onResize : function(){
40745         if(this.monitorResize){
40746             this.autoSizeTabs();
40747         }
40748     },
40749
40750     /**
40751      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40752      */
40753     beginUpdate : function(){
40754         this.updating = true;
40755     },
40756
40757     /**
40758      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40759      */
40760     endUpdate : function(){
40761         this.updating = false;
40762         this.autoSizeTabs();
40763     },
40764
40765     /**
40766      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40767      */
40768     autoSizeTabs : function()
40769     {
40770         var count = this.items.length;
40771         var vcount = count - this.hiddenCount;
40772         
40773         if (vcount < 2) {
40774             this.stripEl.hide();
40775         } else {
40776             this.stripEl.show();
40777         }
40778         
40779         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40780             return;
40781         }
40782         
40783         
40784         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40785         var availWidth = Math.floor(w / vcount);
40786         var b = this.stripBody;
40787         if(b.getWidth() > w){
40788             var tabs = this.items;
40789             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40790             if(availWidth < this.minTabWidth){
40791                 /*if(!this.sleft){    // incomplete scrolling code
40792                     this.createScrollButtons();
40793                 }
40794                 this.showScroll();
40795                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40796             }
40797         }else{
40798             if(this.currentTabWidth < this.preferredTabWidth){
40799                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40800             }
40801         }
40802     },
40803
40804     /**
40805      * Returns the number of tabs in this TabPanel.
40806      * @return {Number}
40807      */
40808      getCount : function(){
40809          return this.items.length;
40810      },
40811
40812     /**
40813      * Resizes all the tabs to the passed width
40814      * @param {Number} The new width
40815      */
40816     setTabWidth : function(width){
40817         this.currentTabWidth = width;
40818         for(var i = 0, len = this.items.length; i < len; i++) {
40819                 if(!this.items[i].isHidden()) {
40820                 this.items[i].setWidth(width);
40821             }
40822         }
40823     },
40824
40825     /**
40826      * Destroys this TabPanel
40827      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40828      */
40829     destroy : function(removeEl){
40830         Roo.EventManager.removeResizeListener(this.onResize, this);
40831         for(var i = 0, len = this.items.length; i < len; i++){
40832             this.items[i].purgeListeners();
40833         }
40834         if(removeEl === true){
40835             this.el.update("");
40836             this.el.remove();
40837         }
40838     },
40839     
40840     createStrip : function(container)
40841     {
40842         var strip = document.createElement("nav");
40843         strip.className = Roo.bootstrap.version == 4 ?
40844             "navbar-light bg-light" : 
40845             "navbar navbar-default"; //"x-tabs-wrap";
40846         container.appendChild(strip);
40847         return strip;
40848     },
40849     
40850     createStripList : function(strip)
40851     {
40852         // div wrapper for retard IE
40853         // returns the "tr" element.
40854         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40855         //'<div class="x-tabs-strip-wrap">'+
40856           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40857           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40858         return strip.firstChild; //.firstChild.firstChild.firstChild;
40859     },
40860     createBody : function(container)
40861     {
40862         var body = document.createElement("div");
40863         Roo.id(body, "tab-body");
40864         //Roo.fly(body).addClass("x-tabs-body");
40865         Roo.fly(body).addClass("tab-content");
40866         container.appendChild(body);
40867         return body;
40868     },
40869     createItemBody :function(bodyEl, id){
40870         var body = Roo.getDom(id);
40871         if(!body){
40872             body = document.createElement("div");
40873             body.id = id;
40874         }
40875         //Roo.fly(body).addClass("x-tabs-item-body");
40876         Roo.fly(body).addClass("tab-pane");
40877          bodyEl.insertBefore(body, bodyEl.firstChild);
40878         return body;
40879     },
40880     /** @private */
40881     createStripElements :  function(stripEl, text, closable, tpl)
40882     {
40883         var td = document.createElement("li"); // was td..
40884         td.className = 'nav-item';
40885         
40886         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40887         
40888         
40889         stripEl.appendChild(td);
40890         /*if(closable){
40891             td.className = "x-tabs-closable";
40892             if(!this.closeTpl){
40893                 this.closeTpl = new Roo.Template(
40894                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40895                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40896                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40897                 );
40898             }
40899             var el = this.closeTpl.overwrite(td, {"text": text});
40900             var close = el.getElementsByTagName("div")[0];
40901             var inner = el.getElementsByTagName("em")[0];
40902             return {"el": el, "close": close, "inner": inner};
40903         } else {
40904         */
40905         // not sure what this is..
40906 //            if(!this.tabTpl){
40907                 //this.tabTpl = new Roo.Template(
40908                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40909                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40910                 //);
40911 //                this.tabTpl = new Roo.Template(
40912 //                   '<a href="#">' +
40913 //                   '<span unselectable="on"' +
40914 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40915 //                            ' >{text}</span></a>'
40916 //                );
40917 //                
40918 //            }
40919
40920
40921             var template = tpl || this.tabTpl || false;
40922             
40923             if(!template){
40924                 template =  new Roo.Template(
40925                         Roo.bootstrap.version == 4 ? 
40926                             (
40927                                 '<a class="nav-link" href="#" unselectable="on"' +
40928                                      (this.disableTooltips ? '' : ' title="{text}"') +
40929                                      ' >{text}</a>'
40930                             ) : (
40931                                 '<a class="nav-link" href="#">' +
40932                                 '<span unselectable="on"' +
40933                                          (this.disableTooltips ? '' : ' title="{text}"') +
40934                                     ' >{text}</span></a>'
40935                             )
40936                 );
40937             }
40938             
40939             switch (typeof(template)) {
40940                 case 'object' :
40941                     break;
40942                 case 'string' :
40943                     template = new Roo.Template(template);
40944                     break;
40945                 default :
40946                     break;
40947             }
40948             
40949             var el = template.overwrite(td, {"text": text});
40950             
40951             var inner = el.getElementsByTagName("span")[0];
40952             
40953             return {"el": el, "inner": inner};
40954             
40955     }
40956         
40957     
40958 });
40959
40960 /**
40961  * @class Roo.TabPanelItem
40962  * @extends Roo.util.Observable
40963  * Represents an individual item (tab plus body) in a TabPanel.
40964  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40965  * @param {String} id The id of this TabPanelItem
40966  * @param {String} text The text for the tab of this TabPanelItem
40967  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40968  */
40969 Roo.bootstrap.panel.TabItem = function(config){
40970     /**
40971      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40972      * @type Roo.TabPanel
40973      */
40974     this.tabPanel = config.panel;
40975     /**
40976      * The id for this TabPanelItem
40977      * @type String
40978      */
40979     this.id = config.id;
40980     /** @private */
40981     this.disabled = false;
40982     /** @private */
40983     this.text = config.text;
40984     /** @private */
40985     this.loaded = false;
40986     this.closable = config.closable;
40987
40988     /**
40989      * The body element for this TabPanelItem.
40990      * @type Roo.Element
40991      */
40992     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40993     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40994     this.bodyEl.setStyle("display", "block");
40995     this.bodyEl.setStyle("zoom", "1");
40996     //this.hideAction();
40997
40998     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40999     /** @private */
41000     this.el = Roo.get(els.el);
41001     this.inner = Roo.get(els.inner, true);
41002      this.textEl = Roo.bootstrap.version == 4 ?
41003         this.el : Roo.get(this.el.dom.firstChild, true);
41004
41005     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41006     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41007
41008     
41009 //    this.el.on("mousedown", this.onTabMouseDown, this);
41010     this.el.on("click", this.onTabClick, this);
41011     /** @private */
41012     if(config.closable){
41013         var c = Roo.get(els.close, true);
41014         c.dom.title = this.closeText;
41015         c.addClassOnOver("close-over");
41016         c.on("click", this.closeClick, this);
41017      }
41018
41019     this.addEvents({
41020          /**
41021          * @event activate
41022          * Fires when this tab becomes the active tab.
41023          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41024          * @param {Roo.TabPanelItem} this
41025          */
41026         "activate": true,
41027         /**
41028          * @event beforeclose
41029          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41030          * @param {Roo.TabPanelItem} this
41031          * @param {Object} e Set cancel to true on this object to cancel the close.
41032          */
41033         "beforeclose": true,
41034         /**
41035          * @event close
41036          * Fires when this tab is closed.
41037          * @param {Roo.TabPanelItem} this
41038          */
41039          "close": true,
41040         /**
41041          * @event deactivate
41042          * Fires when this tab is no longer the active tab.
41043          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41044          * @param {Roo.TabPanelItem} this
41045          */
41046          "deactivate" : true
41047     });
41048     this.hidden = false;
41049
41050     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41051 };
41052
41053 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41054            {
41055     purgeListeners : function(){
41056        Roo.util.Observable.prototype.purgeListeners.call(this);
41057        this.el.removeAllListeners();
41058     },
41059     /**
41060      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41061      */
41062     show : function(){
41063         this.status_node.addClass("active");
41064         this.showAction();
41065         if(Roo.isOpera){
41066             this.tabPanel.stripWrap.repaint();
41067         }
41068         this.fireEvent("activate", this.tabPanel, this);
41069     },
41070
41071     /**
41072      * Returns true if this tab is the active tab.
41073      * @return {Boolean}
41074      */
41075     isActive : function(){
41076         return this.tabPanel.getActiveTab() == this;
41077     },
41078
41079     /**
41080      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41081      */
41082     hide : function(){
41083         this.status_node.removeClass("active");
41084         this.hideAction();
41085         this.fireEvent("deactivate", this.tabPanel, this);
41086     },
41087
41088     hideAction : function(){
41089         this.bodyEl.hide();
41090         this.bodyEl.setStyle("position", "absolute");
41091         this.bodyEl.setLeft("-20000px");
41092         this.bodyEl.setTop("-20000px");
41093     },
41094
41095     showAction : function(){
41096         this.bodyEl.setStyle("position", "relative");
41097         this.bodyEl.setTop("");
41098         this.bodyEl.setLeft("");
41099         this.bodyEl.show();
41100     },
41101
41102     /**
41103      * Set the tooltip for the tab.
41104      * @param {String} tooltip The tab's tooltip
41105      */
41106     setTooltip : function(text){
41107         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41108             this.textEl.dom.qtip = text;
41109             this.textEl.dom.removeAttribute('title');
41110         }else{
41111             this.textEl.dom.title = text;
41112         }
41113     },
41114
41115     onTabClick : function(e){
41116         e.preventDefault();
41117         this.tabPanel.activate(this.id);
41118     },
41119
41120     onTabMouseDown : function(e){
41121         e.preventDefault();
41122         this.tabPanel.activate(this.id);
41123     },
41124 /*
41125     getWidth : function(){
41126         return this.inner.getWidth();
41127     },
41128
41129     setWidth : function(width){
41130         var iwidth = width - this.linode.getPadding("lr");
41131         this.inner.setWidth(iwidth);
41132         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41133         this.linode.setWidth(width);
41134     },
41135 */
41136     /**
41137      * Show or hide the tab
41138      * @param {Boolean} hidden True to hide or false to show.
41139      */
41140     setHidden : function(hidden){
41141         this.hidden = hidden;
41142         this.linode.setStyle("display", hidden ? "none" : "");
41143     },
41144
41145     /**
41146      * Returns true if this tab is "hidden"
41147      * @return {Boolean}
41148      */
41149     isHidden : function(){
41150         return this.hidden;
41151     },
41152
41153     /**
41154      * Returns the text for this tab
41155      * @return {String}
41156      */
41157     getText : function(){
41158         return this.text;
41159     },
41160     /*
41161     autoSize : function(){
41162         //this.el.beginMeasure();
41163         this.textEl.setWidth(1);
41164         /*
41165          *  #2804 [new] Tabs in Roojs
41166          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41167          */
41168         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41169         //this.el.endMeasure();
41170     //},
41171
41172     /**
41173      * Sets the text for the tab (Note: this also sets the tooltip text)
41174      * @param {String} text The tab's text and tooltip
41175      */
41176     setText : function(text){
41177         this.text = text;
41178         this.textEl.update(text);
41179         this.setTooltip(text);
41180         //if(!this.tabPanel.resizeTabs){
41181         //    this.autoSize();
41182         //}
41183     },
41184     /**
41185      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41186      */
41187     activate : function(){
41188         this.tabPanel.activate(this.id);
41189     },
41190
41191     /**
41192      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41193      */
41194     disable : function(){
41195         if(this.tabPanel.active != this){
41196             this.disabled = true;
41197             this.status_node.addClass("disabled");
41198         }
41199     },
41200
41201     /**
41202      * Enables this TabPanelItem if it was previously disabled.
41203      */
41204     enable : function(){
41205         this.disabled = false;
41206         this.status_node.removeClass("disabled");
41207     },
41208
41209     /**
41210      * Sets the content for this TabPanelItem.
41211      * @param {String} content The content
41212      * @param {Boolean} loadScripts true to look for and load scripts
41213      */
41214     setContent : function(content, loadScripts){
41215         this.bodyEl.update(content, loadScripts);
41216     },
41217
41218     /**
41219      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41220      * @return {Roo.UpdateManager} The UpdateManager
41221      */
41222     getUpdateManager : function(){
41223         return this.bodyEl.getUpdateManager();
41224     },
41225
41226     /**
41227      * Set a URL to be used to load the content for this TabPanelItem.
41228      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41229      * @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)
41230      * @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)
41231      * @return {Roo.UpdateManager} The UpdateManager
41232      */
41233     setUrl : function(url, params, loadOnce){
41234         if(this.refreshDelegate){
41235             this.un('activate', this.refreshDelegate);
41236         }
41237         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41238         this.on("activate", this.refreshDelegate);
41239         return this.bodyEl.getUpdateManager();
41240     },
41241
41242     /** @private */
41243     _handleRefresh : function(url, params, loadOnce){
41244         if(!loadOnce || !this.loaded){
41245             var updater = this.bodyEl.getUpdateManager();
41246             updater.update(url, params, this._setLoaded.createDelegate(this));
41247         }
41248     },
41249
41250     /**
41251      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41252      *   Will fail silently if the setUrl method has not been called.
41253      *   This does not activate the panel, just updates its content.
41254      */
41255     refresh : function(){
41256         if(this.refreshDelegate){
41257            this.loaded = false;
41258            this.refreshDelegate();
41259         }
41260     },
41261
41262     /** @private */
41263     _setLoaded : function(){
41264         this.loaded = true;
41265     },
41266
41267     /** @private */
41268     closeClick : function(e){
41269         var o = {};
41270         e.stopEvent();
41271         this.fireEvent("beforeclose", this, o);
41272         if(o.cancel !== true){
41273             this.tabPanel.removeTab(this.id);
41274         }
41275     },
41276     /**
41277      * The text displayed in the tooltip for the close icon.
41278      * @type String
41279      */
41280     closeText : "Close this tab"
41281 });
41282 /**
41283 *    This script refer to:
41284 *    Title: International Telephone Input
41285 *    Author: Jack O'Connor
41286 *    Code version:  v12.1.12
41287 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41288 **/
41289
41290 Roo.bootstrap.PhoneInputData = function() {
41291     var d = [
41292       [
41293         "Afghanistan (‫افغانستان‬‎)",
41294         "af",
41295         "93"
41296       ],
41297       [
41298         "Albania (Shqipëri)",
41299         "al",
41300         "355"
41301       ],
41302       [
41303         "Algeria (‫الجزائر‬‎)",
41304         "dz",
41305         "213"
41306       ],
41307       [
41308         "American Samoa",
41309         "as",
41310         "1684"
41311       ],
41312       [
41313         "Andorra",
41314         "ad",
41315         "376"
41316       ],
41317       [
41318         "Angola",
41319         "ao",
41320         "244"
41321       ],
41322       [
41323         "Anguilla",
41324         "ai",
41325         "1264"
41326       ],
41327       [
41328         "Antigua and Barbuda",
41329         "ag",
41330         "1268"
41331       ],
41332       [
41333         "Argentina",
41334         "ar",
41335         "54"
41336       ],
41337       [
41338         "Armenia (Հայաստան)",
41339         "am",
41340         "374"
41341       ],
41342       [
41343         "Aruba",
41344         "aw",
41345         "297"
41346       ],
41347       [
41348         "Australia",
41349         "au",
41350         "61",
41351         0
41352       ],
41353       [
41354         "Austria (Österreich)",
41355         "at",
41356         "43"
41357       ],
41358       [
41359         "Azerbaijan (Azərbaycan)",
41360         "az",
41361         "994"
41362       ],
41363       [
41364         "Bahamas",
41365         "bs",
41366         "1242"
41367       ],
41368       [
41369         "Bahrain (‫البحرين‬‎)",
41370         "bh",
41371         "973"
41372       ],
41373       [
41374         "Bangladesh (বাংলাদেশ)",
41375         "bd",
41376         "880"
41377       ],
41378       [
41379         "Barbados",
41380         "bb",
41381         "1246"
41382       ],
41383       [
41384         "Belarus (Беларусь)",
41385         "by",
41386         "375"
41387       ],
41388       [
41389         "Belgium (België)",
41390         "be",
41391         "32"
41392       ],
41393       [
41394         "Belize",
41395         "bz",
41396         "501"
41397       ],
41398       [
41399         "Benin (Bénin)",
41400         "bj",
41401         "229"
41402       ],
41403       [
41404         "Bermuda",
41405         "bm",
41406         "1441"
41407       ],
41408       [
41409         "Bhutan (འབྲུག)",
41410         "bt",
41411         "975"
41412       ],
41413       [
41414         "Bolivia",
41415         "bo",
41416         "591"
41417       ],
41418       [
41419         "Bosnia and Herzegovina (Босна и Херцеговина)",
41420         "ba",
41421         "387"
41422       ],
41423       [
41424         "Botswana",
41425         "bw",
41426         "267"
41427       ],
41428       [
41429         "Brazil (Brasil)",
41430         "br",
41431         "55"
41432       ],
41433       [
41434         "British Indian Ocean Territory",
41435         "io",
41436         "246"
41437       ],
41438       [
41439         "British Virgin Islands",
41440         "vg",
41441         "1284"
41442       ],
41443       [
41444         "Brunei",
41445         "bn",
41446         "673"
41447       ],
41448       [
41449         "Bulgaria (България)",
41450         "bg",
41451         "359"
41452       ],
41453       [
41454         "Burkina Faso",
41455         "bf",
41456         "226"
41457       ],
41458       [
41459         "Burundi (Uburundi)",
41460         "bi",
41461         "257"
41462       ],
41463       [
41464         "Cambodia (កម្ពុជា)",
41465         "kh",
41466         "855"
41467       ],
41468       [
41469         "Cameroon (Cameroun)",
41470         "cm",
41471         "237"
41472       ],
41473       [
41474         "Canada",
41475         "ca",
41476         "1",
41477         1,
41478         ["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"]
41479       ],
41480       [
41481         "Cape Verde (Kabu Verdi)",
41482         "cv",
41483         "238"
41484       ],
41485       [
41486         "Caribbean Netherlands",
41487         "bq",
41488         "599",
41489         1
41490       ],
41491       [
41492         "Cayman Islands",
41493         "ky",
41494         "1345"
41495       ],
41496       [
41497         "Central African Republic (République centrafricaine)",
41498         "cf",
41499         "236"
41500       ],
41501       [
41502         "Chad (Tchad)",
41503         "td",
41504         "235"
41505       ],
41506       [
41507         "Chile",
41508         "cl",
41509         "56"
41510       ],
41511       [
41512         "China (中国)",
41513         "cn",
41514         "86"
41515       ],
41516       [
41517         "Christmas Island",
41518         "cx",
41519         "61",
41520         2
41521       ],
41522       [
41523         "Cocos (Keeling) Islands",
41524         "cc",
41525         "61",
41526         1
41527       ],
41528       [
41529         "Colombia",
41530         "co",
41531         "57"
41532       ],
41533       [
41534         "Comoros (‫جزر القمر‬‎)",
41535         "km",
41536         "269"
41537       ],
41538       [
41539         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41540         "cd",
41541         "243"
41542       ],
41543       [
41544         "Congo (Republic) (Congo-Brazzaville)",
41545         "cg",
41546         "242"
41547       ],
41548       [
41549         "Cook Islands",
41550         "ck",
41551         "682"
41552       ],
41553       [
41554         "Costa Rica",
41555         "cr",
41556         "506"
41557       ],
41558       [
41559         "Côte d’Ivoire",
41560         "ci",
41561         "225"
41562       ],
41563       [
41564         "Croatia (Hrvatska)",
41565         "hr",
41566         "385"
41567       ],
41568       [
41569         "Cuba",
41570         "cu",
41571         "53"
41572       ],
41573       [
41574         "Curaçao",
41575         "cw",
41576         "599",
41577         0
41578       ],
41579       [
41580         "Cyprus (Κύπρος)",
41581         "cy",
41582         "357"
41583       ],
41584       [
41585         "Czech Republic (Česká republika)",
41586         "cz",
41587         "420"
41588       ],
41589       [
41590         "Denmark (Danmark)",
41591         "dk",
41592         "45"
41593       ],
41594       [
41595         "Djibouti",
41596         "dj",
41597         "253"
41598       ],
41599       [
41600         "Dominica",
41601         "dm",
41602         "1767"
41603       ],
41604       [
41605         "Dominican Republic (República Dominicana)",
41606         "do",
41607         "1",
41608         2,
41609         ["809", "829", "849"]
41610       ],
41611       [
41612         "Ecuador",
41613         "ec",
41614         "593"
41615       ],
41616       [
41617         "Egypt (‫مصر‬‎)",
41618         "eg",
41619         "20"
41620       ],
41621       [
41622         "El Salvador",
41623         "sv",
41624         "503"
41625       ],
41626       [
41627         "Equatorial Guinea (Guinea Ecuatorial)",
41628         "gq",
41629         "240"
41630       ],
41631       [
41632         "Eritrea",
41633         "er",
41634         "291"
41635       ],
41636       [
41637         "Estonia (Eesti)",
41638         "ee",
41639         "372"
41640       ],
41641       [
41642         "Ethiopia",
41643         "et",
41644         "251"
41645       ],
41646       [
41647         "Falkland Islands (Islas Malvinas)",
41648         "fk",
41649         "500"
41650       ],
41651       [
41652         "Faroe Islands (Føroyar)",
41653         "fo",
41654         "298"
41655       ],
41656       [
41657         "Fiji",
41658         "fj",
41659         "679"
41660       ],
41661       [
41662         "Finland (Suomi)",
41663         "fi",
41664         "358",
41665         0
41666       ],
41667       [
41668         "France",
41669         "fr",
41670         "33"
41671       ],
41672       [
41673         "French Guiana (Guyane française)",
41674         "gf",
41675         "594"
41676       ],
41677       [
41678         "French Polynesia (Polynésie française)",
41679         "pf",
41680         "689"
41681       ],
41682       [
41683         "Gabon",
41684         "ga",
41685         "241"
41686       ],
41687       [
41688         "Gambia",
41689         "gm",
41690         "220"
41691       ],
41692       [
41693         "Georgia (საქართველო)",
41694         "ge",
41695         "995"
41696       ],
41697       [
41698         "Germany (Deutschland)",
41699         "de",
41700         "49"
41701       ],
41702       [
41703         "Ghana (Gaana)",
41704         "gh",
41705         "233"
41706       ],
41707       [
41708         "Gibraltar",
41709         "gi",
41710         "350"
41711       ],
41712       [
41713         "Greece (Ελλάδα)",
41714         "gr",
41715         "30"
41716       ],
41717       [
41718         "Greenland (Kalaallit Nunaat)",
41719         "gl",
41720         "299"
41721       ],
41722       [
41723         "Grenada",
41724         "gd",
41725         "1473"
41726       ],
41727       [
41728         "Guadeloupe",
41729         "gp",
41730         "590",
41731         0
41732       ],
41733       [
41734         "Guam",
41735         "gu",
41736         "1671"
41737       ],
41738       [
41739         "Guatemala",
41740         "gt",
41741         "502"
41742       ],
41743       [
41744         "Guernsey",
41745         "gg",
41746         "44",
41747         1
41748       ],
41749       [
41750         "Guinea (Guinée)",
41751         "gn",
41752         "224"
41753       ],
41754       [
41755         "Guinea-Bissau (Guiné Bissau)",
41756         "gw",
41757         "245"
41758       ],
41759       [
41760         "Guyana",
41761         "gy",
41762         "592"
41763       ],
41764       [
41765         "Haiti",
41766         "ht",
41767         "509"
41768       ],
41769       [
41770         "Honduras",
41771         "hn",
41772         "504"
41773       ],
41774       [
41775         "Hong Kong (香港)",
41776         "hk",
41777         "852"
41778       ],
41779       [
41780         "Hungary (Magyarország)",
41781         "hu",
41782         "36"
41783       ],
41784       [
41785         "Iceland (Ísland)",
41786         "is",
41787         "354"
41788       ],
41789       [
41790         "India (भारत)",
41791         "in",
41792         "91"
41793       ],
41794       [
41795         "Indonesia",
41796         "id",
41797         "62"
41798       ],
41799       [
41800         "Iran (‫ایران‬‎)",
41801         "ir",
41802         "98"
41803       ],
41804       [
41805         "Iraq (‫العراق‬‎)",
41806         "iq",
41807         "964"
41808       ],
41809       [
41810         "Ireland",
41811         "ie",
41812         "353"
41813       ],
41814       [
41815         "Isle of Man",
41816         "im",
41817         "44",
41818         2
41819       ],
41820       [
41821         "Israel (‫ישראל‬‎)",
41822         "il",
41823         "972"
41824       ],
41825       [
41826         "Italy (Italia)",
41827         "it",
41828         "39",
41829         0
41830       ],
41831       [
41832         "Jamaica",
41833         "jm",
41834         "1876"
41835       ],
41836       [
41837         "Japan (日本)",
41838         "jp",
41839         "81"
41840       ],
41841       [
41842         "Jersey",
41843         "je",
41844         "44",
41845         3
41846       ],
41847       [
41848         "Jordan (‫الأردن‬‎)",
41849         "jo",
41850         "962"
41851       ],
41852       [
41853         "Kazakhstan (Казахстан)",
41854         "kz",
41855         "7",
41856         1
41857       ],
41858       [
41859         "Kenya",
41860         "ke",
41861         "254"
41862       ],
41863       [
41864         "Kiribati",
41865         "ki",
41866         "686"
41867       ],
41868       [
41869         "Kosovo",
41870         "xk",
41871         "383"
41872       ],
41873       [
41874         "Kuwait (‫الكويت‬‎)",
41875         "kw",
41876         "965"
41877       ],
41878       [
41879         "Kyrgyzstan (Кыргызстан)",
41880         "kg",
41881         "996"
41882       ],
41883       [
41884         "Laos (ລາວ)",
41885         "la",
41886         "856"
41887       ],
41888       [
41889         "Latvia (Latvija)",
41890         "lv",
41891         "371"
41892       ],
41893       [
41894         "Lebanon (‫لبنان‬‎)",
41895         "lb",
41896         "961"
41897       ],
41898       [
41899         "Lesotho",
41900         "ls",
41901         "266"
41902       ],
41903       [
41904         "Liberia",
41905         "lr",
41906         "231"
41907       ],
41908       [
41909         "Libya (‫ليبيا‬‎)",
41910         "ly",
41911         "218"
41912       ],
41913       [
41914         "Liechtenstein",
41915         "li",
41916         "423"
41917       ],
41918       [
41919         "Lithuania (Lietuva)",
41920         "lt",
41921         "370"
41922       ],
41923       [
41924         "Luxembourg",
41925         "lu",
41926         "352"
41927       ],
41928       [
41929         "Macau (澳門)",
41930         "mo",
41931         "853"
41932       ],
41933       [
41934         "Macedonia (FYROM) (Македонија)",
41935         "mk",
41936         "389"
41937       ],
41938       [
41939         "Madagascar (Madagasikara)",
41940         "mg",
41941         "261"
41942       ],
41943       [
41944         "Malawi",
41945         "mw",
41946         "265"
41947       ],
41948       [
41949         "Malaysia",
41950         "my",
41951         "60"
41952       ],
41953       [
41954         "Maldives",
41955         "mv",
41956         "960"
41957       ],
41958       [
41959         "Mali",
41960         "ml",
41961         "223"
41962       ],
41963       [
41964         "Malta",
41965         "mt",
41966         "356"
41967       ],
41968       [
41969         "Marshall Islands",
41970         "mh",
41971         "692"
41972       ],
41973       [
41974         "Martinique",
41975         "mq",
41976         "596"
41977       ],
41978       [
41979         "Mauritania (‫موريتانيا‬‎)",
41980         "mr",
41981         "222"
41982       ],
41983       [
41984         "Mauritius (Moris)",
41985         "mu",
41986         "230"
41987       ],
41988       [
41989         "Mayotte",
41990         "yt",
41991         "262",
41992         1
41993       ],
41994       [
41995         "Mexico (México)",
41996         "mx",
41997         "52"
41998       ],
41999       [
42000         "Micronesia",
42001         "fm",
42002         "691"
42003       ],
42004       [
42005         "Moldova (Republica Moldova)",
42006         "md",
42007         "373"
42008       ],
42009       [
42010         "Monaco",
42011         "mc",
42012         "377"
42013       ],
42014       [
42015         "Mongolia (Монгол)",
42016         "mn",
42017         "976"
42018       ],
42019       [
42020         "Montenegro (Crna Gora)",
42021         "me",
42022         "382"
42023       ],
42024       [
42025         "Montserrat",
42026         "ms",
42027         "1664"
42028       ],
42029       [
42030         "Morocco (‫المغرب‬‎)",
42031         "ma",
42032         "212",
42033         0
42034       ],
42035       [
42036         "Mozambique (Moçambique)",
42037         "mz",
42038         "258"
42039       ],
42040       [
42041         "Myanmar (Burma) (မြန်မာ)",
42042         "mm",
42043         "95"
42044       ],
42045       [
42046         "Namibia (Namibië)",
42047         "na",
42048         "264"
42049       ],
42050       [
42051         "Nauru",
42052         "nr",
42053         "674"
42054       ],
42055       [
42056         "Nepal (नेपाल)",
42057         "np",
42058         "977"
42059       ],
42060       [
42061         "Netherlands (Nederland)",
42062         "nl",
42063         "31"
42064       ],
42065       [
42066         "New Caledonia (Nouvelle-Calédonie)",
42067         "nc",
42068         "687"
42069       ],
42070       [
42071         "New Zealand",
42072         "nz",
42073         "64"
42074       ],
42075       [
42076         "Nicaragua",
42077         "ni",
42078         "505"
42079       ],
42080       [
42081         "Niger (Nijar)",
42082         "ne",
42083         "227"
42084       ],
42085       [
42086         "Nigeria",
42087         "ng",
42088         "234"
42089       ],
42090       [
42091         "Niue",
42092         "nu",
42093         "683"
42094       ],
42095       [
42096         "Norfolk Island",
42097         "nf",
42098         "672"
42099       ],
42100       [
42101         "North Korea (조선 민주주의 인민 공화국)",
42102         "kp",
42103         "850"
42104       ],
42105       [
42106         "Northern Mariana Islands",
42107         "mp",
42108         "1670"
42109       ],
42110       [
42111         "Norway (Norge)",
42112         "no",
42113         "47",
42114         0
42115       ],
42116       [
42117         "Oman (‫عُمان‬‎)",
42118         "om",
42119         "968"
42120       ],
42121       [
42122         "Pakistan (‫پاکستان‬‎)",
42123         "pk",
42124         "92"
42125       ],
42126       [
42127         "Palau",
42128         "pw",
42129         "680"
42130       ],
42131       [
42132         "Palestine (‫فلسطين‬‎)",
42133         "ps",
42134         "970"
42135       ],
42136       [
42137         "Panama (Panamá)",
42138         "pa",
42139         "507"
42140       ],
42141       [
42142         "Papua New Guinea",
42143         "pg",
42144         "675"
42145       ],
42146       [
42147         "Paraguay",
42148         "py",
42149         "595"
42150       ],
42151       [
42152         "Peru (Perú)",
42153         "pe",
42154         "51"
42155       ],
42156       [
42157         "Philippines",
42158         "ph",
42159         "63"
42160       ],
42161       [
42162         "Poland (Polska)",
42163         "pl",
42164         "48"
42165       ],
42166       [
42167         "Portugal",
42168         "pt",
42169         "351"
42170       ],
42171       [
42172         "Puerto Rico",
42173         "pr",
42174         "1",
42175         3,
42176         ["787", "939"]
42177       ],
42178       [
42179         "Qatar (‫قطر‬‎)",
42180         "qa",
42181         "974"
42182       ],
42183       [
42184         "Réunion (La Réunion)",
42185         "re",
42186         "262",
42187         0
42188       ],
42189       [
42190         "Romania (România)",
42191         "ro",
42192         "40"
42193       ],
42194       [
42195         "Russia (Россия)",
42196         "ru",
42197         "7",
42198         0
42199       ],
42200       [
42201         "Rwanda",
42202         "rw",
42203         "250"
42204       ],
42205       [
42206         "Saint Barthélemy",
42207         "bl",
42208         "590",
42209         1
42210       ],
42211       [
42212         "Saint Helena",
42213         "sh",
42214         "290"
42215       ],
42216       [
42217         "Saint Kitts and Nevis",
42218         "kn",
42219         "1869"
42220       ],
42221       [
42222         "Saint Lucia",
42223         "lc",
42224         "1758"
42225       ],
42226       [
42227         "Saint Martin (Saint-Martin (partie française))",
42228         "mf",
42229         "590",
42230         2
42231       ],
42232       [
42233         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42234         "pm",
42235         "508"
42236       ],
42237       [
42238         "Saint Vincent and the Grenadines",
42239         "vc",
42240         "1784"
42241       ],
42242       [
42243         "Samoa",
42244         "ws",
42245         "685"
42246       ],
42247       [
42248         "San Marino",
42249         "sm",
42250         "378"
42251       ],
42252       [
42253         "São Tomé and Príncipe (São Tomé e Príncipe)",
42254         "st",
42255         "239"
42256       ],
42257       [
42258         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42259         "sa",
42260         "966"
42261       ],
42262       [
42263         "Senegal (Sénégal)",
42264         "sn",
42265         "221"
42266       ],
42267       [
42268         "Serbia (Србија)",
42269         "rs",
42270         "381"
42271       ],
42272       [
42273         "Seychelles",
42274         "sc",
42275         "248"
42276       ],
42277       [
42278         "Sierra Leone",
42279         "sl",
42280         "232"
42281       ],
42282       [
42283         "Singapore",
42284         "sg",
42285         "65"
42286       ],
42287       [
42288         "Sint Maarten",
42289         "sx",
42290         "1721"
42291       ],
42292       [
42293         "Slovakia (Slovensko)",
42294         "sk",
42295         "421"
42296       ],
42297       [
42298         "Slovenia (Slovenija)",
42299         "si",
42300         "386"
42301       ],
42302       [
42303         "Solomon Islands",
42304         "sb",
42305         "677"
42306       ],
42307       [
42308         "Somalia (Soomaaliya)",
42309         "so",
42310         "252"
42311       ],
42312       [
42313         "South Africa",
42314         "za",
42315         "27"
42316       ],
42317       [
42318         "South Korea (대한민국)",
42319         "kr",
42320         "82"
42321       ],
42322       [
42323         "South Sudan (‫جنوب السودان‬‎)",
42324         "ss",
42325         "211"
42326       ],
42327       [
42328         "Spain (España)",
42329         "es",
42330         "34"
42331       ],
42332       [
42333         "Sri Lanka (ශ්‍රී ලංකාව)",
42334         "lk",
42335         "94"
42336       ],
42337       [
42338         "Sudan (‫السودان‬‎)",
42339         "sd",
42340         "249"
42341       ],
42342       [
42343         "Suriname",
42344         "sr",
42345         "597"
42346       ],
42347       [
42348         "Svalbard and Jan Mayen",
42349         "sj",
42350         "47",
42351         1
42352       ],
42353       [
42354         "Swaziland",
42355         "sz",
42356         "268"
42357       ],
42358       [
42359         "Sweden (Sverige)",
42360         "se",
42361         "46"
42362       ],
42363       [
42364         "Switzerland (Schweiz)",
42365         "ch",
42366         "41"
42367       ],
42368       [
42369         "Syria (‫سوريا‬‎)",
42370         "sy",
42371         "963"
42372       ],
42373       [
42374         "Taiwan (台灣)",
42375         "tw",
42376         "886"
42377       ],
42378       [
42379         "Tajikistan",
42380         "tj",
42381         "992"
42382       ],
42383       [
42384         "Tanzania",
42385         "tz",
42386         "255"
42387       ],
42388       [
42389         "Thailand (ไทย)",
42390         "th",
42391         "66"
42392       ],
42393       [
42394         "Timor-Leste",
42395         "tl",
42396         "670"
42397       ],
42398       [
42399         "Togo",
42400         "tg",
42401         "228"
42402       ],
42403       [
42404         "Tokelau",
42405         "tk",
42406         "690"
42407       ],
42408       [
42409         "Tonga",
42410         "to",
42411         "676"
42412       ],
42413       [
42414         "Trinidad and Tobago",
42415         "tt",
42416         "1868"
42417       ],
42418       [
42419         "Tunisia (‫تونس‬‎)",
42420         "tn",
42421         "216"
42422       ],
42423       [
42424         "Turkey (Türkiye)",
42425         "tr",
42426         "90"
42427       ],
42428       [
42429         "Turkmenistan",
42430         "tm",
42431         "993"
42432       ],
42433       [
42434         "Turks and Caicos Islands",
42435         "tc",
42436         "1649"
42437       ],
42438       [
42439         "Tuvalu",
42440         "tv",
42441         "688"
42442       ],
42443       [
42444         "U.S. Virgin Islands",
42445         "vi",
42446         "1340"
42447       ],
42448       [
42449         "Uganda",
42450         "ug",
42451         "256"
42452       ],
42453       [
42454         "Ukraine (Україна)",
42455         "ua",
42456         "380"
42457       ],
42458       [
42459         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42460         "ae",
42461         "971"
42462       ],
42463       [
42464         "United Kingdom",
42465         "gb",
42466         "44",
42467         0
42468       ],
42469       [
42470         "United States",
42471         "us",
42472         "1",
42473         0
42474       ],
42475       [
42476         "Uruguay",
42477         "uy",
42478         "598"
42479       ],
42480       [
42481         "Uzbekistan (Oʻzbekiston)",
42482         "uz",
42483         "998"
42484       ],
42485       [
42486         "Vanuatu",
42487         "vu",
42488         "678"
42489       ],
42490       [
42491         "Vatican City (Città del Vaticano)",
42492         "va",
42493         "39",
42494         1
42495       ],
42496       [
42497         "Venezuela",
42498         "ve",
42499         "58"
42500       ],
42501       [
42502         "Vietnam (Việt Nam)",
42503         "vn",
42504         "84"
42505       ],
42506       [
42507         "Wallis and Futuna (Wallis-et-Futuna)",
42508         "wf",
42509         "681"
42510       ],
42511       [
42512         "Western Sahara (‫الصحراء الغربية‬‎)",
42513         "eh",
42514         "212",
42515         1
42516       ],
42517       [
42518         "Yemen (‫اليمن‬‎)",
42519         "ye",
42520         "967"
42521       ],
42522       [
42523         "Zambia",
42524         "zm",
42525         "260"
42526       ],
42527       [
42528         "Zimbabwe",
42529         "zw",
42530         "263"
42531       ],
42532       [
42533         "Åland Islands",
42534         "ax",
42535         "358",
42536         1
42537       ]
42538   ];
42539   
42540   return d;
42541 }/**
42542 *    This script refer to:
42543 *    Title: International Telephone Input
42544 *    Author: Jack O'Connor
42545 *    Code version:  v12.1.12
42546 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42547 **/
42548
42549 /**
42550  * @class Roo.bootstrap.PhoneInput
42551  * @extends Roo.bootstrap.TriggerField
42552  * An input with International dial-code selection
42553  
42554  * @cfg {String} defaultDialCode default '+852'
42555  * @cfg {Array} preferedCountries default []
42556   
42557  * @constructor
42558  * Create a new PhoneInput.
42559  * @param {Object} config Configuration options
42560  */
42561
42562 Roo.bootstrap.PhoneInput = function(config) {
42563     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42564 };
42565
42566 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42567         
42568         listWidth: undefined,
42569         
42570         selectedClass: 'active',
42571         
42572         invalidClass : "has-warning",
42573         
42574         validClass: 'has-success',
42575         
42576         allowed: '0123456789',
42577         
42578         max_length: 15,
42579         
42580         /**
42581          * @cfg {String} defaultDialCode The default dial code when initializing the input
42582          */
42583         defaultDialCode: '+852',
42584         
42585         /**
42586          * @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
42587          */
42588         preferedCountries: false,
42589         
42590         getAutoCreate : function()
42591         {
42592             var data = Roo.bootstrap.PhoneInputData();
42593             var align = this.labelAlign || this.parentLabelAlign();
42594             var id = Roo.id();
42595             
42596             this.allCountries = [];
42597             this.dialCodeMapping = [];
42598             
42599             for (var i = 0; i < data.length; i++) {
42600               var c = data[i];
42601               this.allCountries[i] = {
42602                 name: c[0],
42603                 iso2: c[1],
42604                 dialCode: c[2],
42605                 priority: c[3] || 0,
42606                 areaCodes: c[4] || null
42607               };
42608               this.dialCodeMapping[c[2]] = {
42609                   name: c[0],
42610                   iso2: c[1],
42611                   priority: c[3] || 0,
42612                   areaCodes: c[4] || null
42613               };
42614             }
42615             
42616             var cfg = {
42617                 cls: 'form-group',
42618                 cn: []
42619             };
42620             
42621             var input =  {
42622                 tag: 'input',
42623                 id : id,
42624                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42625                 maxlength: this.max_length,
42626                 cls : 'form-control tel-input',
42627                 autocomplete: 'new-password'
42628             };
42629             
42630             var hiddenInput = {
42631                 tag: 'input',
42632                 type: 'hidden',
42633                 cls: 'hidden-tel-input'
42634             };
42635             
42636             if (this.name) {
42637                 hiddenInput.name = this.name;
42638             }
42639             
42640             if (this.disabled) {
42641                 input.disabled = true;
42642             }
42643             
42644             var flag_container = {
42645                 tag: 'div',
42646                 cls: 'flag-box',
42647                 cn: [
42648                     {
42649                         tag: 'div',
42650                         cls: 'flag'
42651                     },
42652                     {
42653                         tag: 'div',
42654                         cls: 'caret'
42655                     }
42656                 ]
42657             };
42658             
42659             var box = {
42660                 tag: 'div',
42661                 cls: this.hasFeedback ? 'has-feedback' : '',
42662                 cn: [
42663                     hiddenInput,
42664                     input,
42665                     {
42666                         tag: 'input',
42667                         cls: 'dial-code-holder',
42668                         disabled: true
42669                     }
42670                 ]
42671             };
42672             
42673             var container = {
42674                 cls: 'roo-select2-container input-group',
42675                 cn: [
42676                     flag_container,
42677                     box
42678                 ]
42679             };
42680             
42681             if (this.fieldLabel.length) {
42682                 var indicator = {
42683                     tag: 'i',
42684                     tooltip: 'This field is required'
42685                 };
42686                 
42687                 var label = {
42688                     tag: 'label',
42689                     'for':  id,
42690                     cls: 'control-label',
42691                     cn: []
42692                 };
42693                 
42694                 var label_text = {
42695                     tag: 'span',
42696                     html: this.fieldLabel
42697                 };
42698                 
42699                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42700                 label.cn = [
42701                     indicator,
42702                     label_text
42703                 ];
42704                 
42705                 if(this.indicatorpos == 'right') {
42706                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42707                     label.cn = [
42708                         label_text,
42709                         indicator
42710                     ];
42711                 }
42712                 
42713                 if(align == 'left') {
42714                     container = {
42715                         tag: 'div',
42716                         cn: [
42717                             container
42718                         ]
42719                     };
42720                     
42721                     if(this.labelWidth > 12){
42722                         label.style = "width: " + this.labelWidth + 'px';
42723                     }
42724                     if(this.labelWidth < 13 && this.labelmd == 0){
42725                         this.labelmd = this.labelWidth;
42726                     }
42727                     if(this.labellg > 0){
42728                         label.cls += ' col-lg-' + this.labellg;
42729                         input.cls += ' col-lg-' + (12 - this.labellg);
42730                     }
42731                     if(this.labelmd > 0){
42732                         label.cls += ' col-md-' + this.labelmd;
42733                         container.cls += ' col-md-' + (12 - this.labelmd);
42734                     }
42735                     if(this.labelsm > 0){
42736                         label.cls += ' col-sm-' + this.labelsm;
42737                         container.cls += ' col-sm-' + (12 - this.labelsm);
42738                     }
42739                     if(this.labelxs > 0){
42740                         label.cls += ' col-xs-' + this.labelxs;
42741                         container.cls += ' col-xs-' + (12 - this.labelxs);
42742                     }
42743                 }
42744             }
42745             
42746             cfg.cn = [
42747                 label,
42748                 container
42749             ];
42750             
42751             var settings = this;
42752             
42753             ['xs','sm','md','lg'].map(function(size){
42754                 if (settings[size]) {
42755                     cfg.cls += ' col-' + size + '-' + settings[size];
42756                 }
42757             });
42758             
42759             this.store = new Roo.data.Store({
42760                 proxy : new Roo.data.MemoryProxy({}),
42761                 reader : new Roo.data.JsonReader({
42762                     fields : [
42763                         {
42764                             'name' : 'name',
42765                             'type' : 'string'
42766                         },
42767                         {
42768                             'name' : 'iso2',
42769                             'type' : 'string'
42770                         },
42771                         {
42772                             'name' : 'dialCode',
42773                             'type' : 'string'
42774                         },
42775                         {
42776                             'name' : 'priority',
42777                             'type' : 'string'
42778                         },
42779                         {
42780                             'name' : 'areaCodes',
42781                             'type' : 'string'
42782                         }
42783                     ]
42784                 })
42785             });
42786             
42787             if(!this.preferedCountries) {
42788                 this.preferedCountries = [
42789                     'hk',
42790                     'gb',
42791                     'us'
42792                 ];
42793             }
42794             
42795             var p = this.preferedCountries.reverse();
42796             
42797             if(p) {
42798                 for (var i = 0; i < p.length; i++) {
42799                     for (var j = 0; j < this.allCountries.length; j++) {
42800                         if(this.allCountries[j].iso2 == p[i]) {
42801                             var t = this.allCountries[j];
42802                             this.allCountries.splice(j,1);
42803                             this.allCountries.unshift(t);
42804                         }
42805                     } 
42806                 }
42807             }
42808             
42809             this.store.proxy.data = {
42810                 success: true,
42811                 data: this.allCountries
42812             };
42813             
42814             return cfg;
42815         },
42816         
42817         initEvents : function()
42818         {
42819             this.createList();
42820             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42821             
42822             this.indicator = this.indicatorEl();
42823             this.flag = this.flagEl();
42824             this.dialCodeHolder = this.dialCodeHolderEl();
42825             
42826             this.trigger = this.el.select('div.flag-box',true).first();
42827             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42828             
42829             var _this = this;
42830             
42831             (function(){
42832                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42833                 _this.list.setWidth(lw);
42834             }).defer(100);
42835             
42836             this.list.on('mouseover', this.onViewOver, this);
42837             this.list.on('mousemove', this.onViewMove, this);
42838             this.inputEl().on("keyup", this.onKeyUp, this);
42839             this.inputEl().on("keypress", this.onKeyPress, this);
42840             
42841             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42842
42843             this.view = new Roo.View(this.list, this.tpl, {
42844                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42845             });
42846             
42847             this.view.on('click', this.onViewClick, this);
42848             this.setValue(this.defaultDialCode);
42849         },
42850         
42851         onTriggerClick : function(e)
42852         {
42853             Roo.log('trigger click');
42854             if(this.disabled){
42855                 return;
42856             }
42857             
42858             if(this.isExpanded()){
42859                 this.collapse();
42860                 this.hasFocus = false;
42861             }else {
42862                 this.store.load({});
42863                 this.hasFocus = true;
42864                 this.expand();
42865             }
42866         },
42867         
42868         isExpanded : function()
42869         {
42870             return this.list.isVisible();
42871         },
42872         
42873         collapse : function()
42874         {
42875             if(!this.isExpanded()){
42876                 return;
42877             }
42878             this.list.hide();
42879             Roo.get(document).un('mousedown', this.collapseIf, this);
42880             Roo.get(document).un('mousewheel', this.collapseIf, this);
42881             this.fireEvent('collapse', this);
42882             this.validate();
42883         },
42884         
42885         expand : function()
42886         {
42887             Roo.log('expand');
42888
42889             if(this.isExpanded() || !this.hasFocus){
42890                 return;
42891             }
42892             
42893             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42894             this.list.setWidth(lw);
42895             
42896             this.list.show();
42897             this.restrictHeight();
42898             
42899             Roo.get(document).on('mousedown', this.collapseIf, this);
42900             Roo.get(document).on('mousewheel', this.collapseIf, this);
42901             
42902             this.fireEvent('expand', this);
42903         },
42904         
42905         restrictHeight : function()
42906         {
42907             this.list.alignTo(this.inputEl(), this.listAlign);
42908             this.list.alignTo(this.inputEl(), this.listAlign);
42909         },
42910         
42911         onViewOver : function(e, t)
42912         {
42913             if(this.inKeyMode){
42914                 return;
42915             }
42916             var item = this.view.findItemFromChild(t);
42917             
42918             if(item){
42919                 var index = this.view.indexOf(item);
42920                 this.select(index, false);
42921             }
42922         },
42923
42924         // private
42925         onViewClick : function(view, doFocus, el, e)
42926         {
42927             var index = this.view.getSelectedIndexes()[0];
42928             
42929             var r = this.store.getAt(index);
42930             
42931             if(r){
42932                 this.onSelect(r, index);
42933             }
42934             if(doFocus !== false && !this.blockFocus){
42935                 this.inputEl().focus();
42936             }
42937         },
42938         
42939         onViewMove : function(e, t)
42940         {
42941             this.inKeyMode = false;
42942         },
42943         
42944         select : function(index, scrollIntoView)
42945         {
42946             this.selectedIndex = index;
42947             this.view.select(index);
42948             if(scrollIntoView !== false){
42949                 var el = this.view.getNode(index);
42950                 if(el){
42951                     this.list.scrollChildIntoView(el, false);
42952                 }
42953             }
42954         },
42955         
42956         createList : function()
42957         {
42958             this.list = Roo.get(document.body).createChild({
42959                 tag: 'ul',
42960                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42961                 style: 'display:none'
42962             });
42963             
42964             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42965         },
42966         
42967         collapseIf : function(e)
42968         {
42969             var in_combo  = e.within(this.el);
42970             var in_list =  e.within(this.list);
42971             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42972             
42973             if (in_combo || in_list || is_list) {
42974                 return;
42975             }
42976             this.collapse();
42977         },
42978         
42979         onSelect : function(record, index)
42980         {
42981             if(this.fireEvent('beforeselect', this, record, index) !== false){
42982                 
42983                 this.setFlagClass(record.data.iso2);
42984                 this.setDialCode(record.data.dialCode);
42985                 this.hasFocus = false;
42986                 this.collapse();
42987                 this.fireEvent('select', this, record, index);
42988             }
42989         },
42990         
42991         flagEl : function()
42992         {
42993             var flag = this.el.select('div.flag',true).first();
42994             if(!flag){
42995                 return false;
42996             }
42997             return flag;
42998         },
42999         
43000         dialCodeHolderEl : function()
43001         {
43002             var d = this.el.select('input.dial-code-holder',true).first();
43003             if(!d){
43004                 return false;
43005             }
43006             return d;
43007         },
43008         
43009         setDialCode : function(v)
43010         {
43011             this.dialCodeHolder.dom.value = '+'+v;
43012         },
43013         
43014         setFlagClass : function(n)
43015         {
43016             this.flag.dom.className = 'flag '+n;
43017         },
43018         
43019         getValue : function()
43020         {
43021             var v = this.inputEl().getValue();
43022             if(this.dialCodeHolder) {
43023                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43024             }
43025             return v;
43026         },
43027         
43028         setValue : function(v)
43029         {
43030             var d = this.getDialCode(v);
43031             
43032             //invalid dial code
43033             if(v.length == 0 || !d || d.length == 0) {
43034                 if(this.rendered){
43035                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43036                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43037                 }
43038                 return;
43039             }
43040             
43041             //valid dial code
43042             this.setFlagClass(this.dialCodeMapping[d].iso2);
43043             this.setDialCode(d);
43044             this.inputEl().dom.value = v.replace('+'+d,'');
43045             this.hiddenEl().dom.value = this.getValue();
43046             
43047             this.validate();
43048         },
43049         
43050         getDialCode : function(v)
43051         {
43052             v = v ||  '';
43053             
43054             if (v.length == 0) {
43055                 return this.dialCodeHolder.dom.value;
43056             }
43057             
43058             var dialCode = "";
43059             if (v.charAt(0) != "+") {
43060                 return false;
43061             }
43062             var numericChars = "";
43063             for (var i = 1; i < v.length; i++) {
43064               var c = v.charAt(i);
43065               if (!isNaN(c)) {
43066                 numericChars += c;
43067                 if (this.dialCodeMapping[numericChars]) {
43068                   dialCode = v.substr(1, i);
43069                 }
43070                 if (numericChars.length == 4) {
43071                   break;
43072                 }
43073               }
43074             }
43075             return dialCode;
43076         },
43077         
43078         reset : function()
43079         {
43080             this.setValue(this.defaultDialCode);
43081             this.validate();
43082         },
43083         
43084         hiddenEl : function()
43085         {
43086             return this.el.select('input.hidden-tel-input',true).first();
43087         },
43088         
43089         // after setting val
43090         onKeyUp : function(e){
43091             this.setValue(this.getValue());
43092         },
43093         
43094         onKeyPress : function(e){
43095             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43096                 e.stopEvent();
43097             }
43098         }
43099         
43100 });
43101 /**
43102  * @class Roo.bootstrap.MoneyField
43103  * @extends Roo.bootstrap.ComboBox
43104  * Bootstrap MoneyField class
43105  * 
43106  * @constructor
43107  * Create a new MoneyField.
43108  * @param {Object} config Configuration options
43109  */
43110
43111 Roo.bootstrap.MoneyField = function(config) {
43112     
43113     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43114     
43115 };
43116
43117 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43118     
43119     /**
43120      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43121      */
43122     allowDecimals : true,
43123     /**
43124      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43125      */
43126     decimalSeparator : ".",
43127     /**
43128      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43129      */
43130     decimalPrecision : 0,
43131     /**
43132      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43133      */
43134     allowNegative : true,
43135     /**
43136      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43137      */
43138     allowZero: true,
43139     /**
43140      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43141      */
43142     minValue : Number.NEGATIVE_INFINITY,
43143     /**
43144      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43145      */
43146     maxValue : Number.MAX_VALUE,
43147     /**
43148      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43149      */
43150     minText : "The minimum value for this field is {0}",
43151     /**
43152      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43153      */
43154     maxText : "The maximum value for this field is {0}",
43155     /**
43156      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43157      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43158      */
43159     nanText : "{0} is not a valid number",
43160     /**
43161      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43162      */
43163     castInt : true,
43164     /**
43165      * @cfg {String} defaults currency of the MoneyField
43166      * value should be in lkey
43167      */
43168     defaultCurrency : false,
43169     /**
43170      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43171      */
43172     thousandsDelimiter : false,
43173     /**
43174      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43175      */
43176     max_length: false,
43177     
43178     inputlg : 9,
43179     inputmd : 9,
43180     inputsm : 9,
43181     inputxs : 6,
43182     
43183     store : false,
43184     
43185     getAutoCreate : function()
43186     {
43187         var align = this.labelAlign || this.parentLabelAlign();
43188         
43189         var id = Roo.id();
43190
43191         var cfg = {
43192             cls: 'form-group',
43193             cn: []
43194         };
43195
43196         var input =  {
43197             tag: 'input',
43198             id : id,
43199             cls : 'form-control roo-money-amount-input',
43200             autocomplete: 'new-password'
43201         };
43202         
43203         var hiddenInput = {
43204             tag: 'input',
43205             type: 'hidden',
43206             id: Roo.id(),
43207             cls: 'hidden-number-input'
43208         };
43209         
43210         if(this.max_length) {
43211             input.maxlength = this.max_length; 
43212         }
43213         
43214         if (this.name) {
43215             hiddenInput.name = this.name;
43216         }
43217
43218         if (this.disabled) {
43219             input.disabled = true;
43220         }
43221
43222         var clg = 12 - this.inputlg;
43223         var cmd = 12 - this.inputmd;
43224         var csm = 12 - this.inputsm;
43225         var cxs = 12 - this.inputxs;
43226         
43227         var container = {
43228             tag : 'div',
43229             cls : 'row roo-money-field',
43230             cn : [
43231                 {
43232                     tag : 'div',
43233                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43234                     cn : [
43235                         {
43236                             tag : 'div',
43237                             cls: 'roo-select2-container input-group',
43238                             cn: [
43239                                 {
43240                                     tag : 'input',
43241                                     cls : 'form-control roo-money-currency-input',
43242                                     autocomplete: 'new-password',
43243                                     readOnly : 1,
43244                                     name : this.currencyName
43245                                 },
43246                                 {
43247                                     tag :'span',
43248                                     cls : 'input-group-addon',
43249                                     cn : [
43250                                         {
43251                                             tag: 'span',
43252                                             cls: 'caret'
43253                                         }
43254                                     ]
43255                                 }
43256                             ]
43257                         }
43258                     ]
43259                 },
43260                 {
43261                     tag : 'div',
43262                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43263                     cn : [
43264                         {
43265                             tag: 'div',
43266                             cls: this.hasFeedback ? 'has-feedback' : '',
43267                             cn: [
43268                                 input
43269                             ]
43270                         }
43271                     ]
43272                 }
43273             ]
43274             
43275         };
43276         
43277         if (this.fieldLabel.length) {
43278             var indicator = {
43279                 tag: 'i',
43280                 tooltip: 'This field is required'
43281             };
43282
43283             var label = {
43284                 tag: 'label',
43285                 'for':  id,
43286                 cls: 'control-label',
43287                 cn: []
43288             };
43289
43290             var label_text = {
43291                 tag: 'span',
43292                 html: this.fieldLabel
43293             };
43294
43295             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43296             label.cn = [
43297                 indicator,
43298                 label_text
43299             ];
43300
43301             if(this.indicatorpos == 'right') {
43302                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43303                 label.cn = [
43304                     label_text,
43305                     indicator
43306                 ];
43307             }
43308
43309             if(align == 'left') {
43310                 container = {
43311                     tag: 'div',
43312                     cn: [
43313                         container
43314                     ]
43315                 };
43316
43317                 if(this.labelWidth > 12){
43318                     label.style = "width: " + this.labelWidth + 'px';
43319                 }
43320                 if(this.labelWidth < 13 && this.labelmd == 0){
43321                     this.labelmd = this.labelWidth;
43322                 }
43323                 if(this.labellg > 0){
43324                     label.cls += ' col-lg-' + this.labellg;
43325                     input.cls += ' col-lg-' + (12 - this.labellg);
43326                 }
43327                 if(this.labelmd > 0){
43328                     label.cls += ' col-md-' + this.labelmd;
43329                     container.cls += ' col-md-' + (12 - this.labelmd);
43330                 }
43331                 if(this.labelsm > 0){
43332                     label.cls += ' col-sm-' + this.labelsm;
43333                     container.cls += ' col-sm-' + (12 - this.labelsm);
43334                 }
43335                 if(this.labelxs > 0){
43336                     label.cls += ' col-xs-' + this.labelxs;
43337                     container.cls += ' col-xs-' + (12 - this.labelxs);
43338                 }
43339             }
43340         }
43341
43342         cfg.cn = [
43343             label,
43344             container,
43345             hiddenInput
43346         ];
43347         
43348         var settings = this;
43349
43350         ['xs','sm','md','lg'].map(function(size){
43351             if (settings[size]) {
43352                 cfg.cls += ' col-' + size + '-' + settings[size];
43353             }
43354         });
43355         
43356         return cfg;
43357     },
43358     
43359     initEvents : function()
43360     {
43361         this.indicator = this.indicatorEl();
43362         
43363         this.initCurrencyEvent();
43364         
43365         this.initNumberEvent();
43366     },
43367     
43368     initCurrencyEvent : function()
43369     {
43370         if (!this.store) {
43371             throw "can not find store for combo";
43372         }
43373         
43374         this.store = Roo.factory(this.store, Roo.data);
43375         this.store.parent = this;
43376         
43377         this.createList();
43378         
43379         this.triggerEl = this.el.select('.input-group-addon', true).first();
43380         
43381         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43382         
43383         var _this = this;
43384         
43385         (function(){
43386             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43387             _this.list.setWidth(lw);
43388         }).defer(100);
43389         
43390         this.list.on('mouseover', this.onViewOver, this);
43391         this.list.on('mousemove', this.onViewMove, this);
43392         this.list.on('scroll', this.onViewScroll, this);
43393         
43394         if(!this.tpl){
43395             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43396         }
43397         
43398         this.view = new Roo.View(this.list, this.tpl, {
43399             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43400         });
43401         
43402         this.view.on('click', this.onViewClick, this);
43403         
43404         this.store.on('beforeload', this.onBeforeLoad, this);
43405         this.store.on('load', this.onLoad, this);
43406         this.store.on('loadexception', this.onLoadException, this);
43407         
43408         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43409             "up" : function(e){
43410                 this.inKeyMode = true;
43411                 this.selectPrev();
43412             },
43413
43414             "down" : function(e){
43415                 if(!this.isExpanded()){
43416                     this.onTriggerClick();
43417                 }else{
43418                     this.inKeyMode = true;
43419                     this.selectNext();
43420                 }
43421             },
43422
43423             "enter" : function(e){
43424                 this.collapse();
43425                 
43426                 if(this.fireEvent("specialkey", this, e)){
43427                     this.onViewClick(false);
43428                 }
43429                 
43430                 return true;
43431             },
43432
43433             "esc" : function(e){
43434                 this.collapse();
43435             },
43436
43437             "tab" : function(e){
43438                 this.collapse();
43439                 
43440                 if(this.fireEvent("specialkey", this, e)){
43441                     this.onViewClick(false);
43442                 }
43443                 
43444                 return true;
43445             },
43446
43447             scope : this,
43448
43449             doRelay : function(foo, bar, hname){
43450                 if(hname == 'down' || this.scope.isExpanded()){
43451                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43452                 }
43453                 return true;
43454             },
43455
43456             forceKeyDown: true
43457         });
43458         
43459         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43460         
43461     },
43462     
43463     initNumberEvent : function(e)
43464     {
43465         this.inputEl().on("keydown" , this.fireKey,  this);
43466         this.inputEl().on("focus", this.onFocus,  this);
43467         this.inputEl().on("blur", this.onBlur,  this);
43468         
43469         this.inputEl().relayEvent('keyup', this);
43470         
43471         if(this.indicator){
43472             this.indicator.addClass('invisible');
43473         }
43474  
43475         this.originalValue = this.getValue();
43476         
43477         if(this.validationEvent == 'keyup'){
43478             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43479             this.inputEl().on('keyup', this.filterValidation, this);
43480         }
43481         else if(this.validationEvent !== false){
43482             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43483         }
43484         
43485         if(this.selectOnFocus){
43486             this.on("focus", this.preFocus, this);
43487             
43488         }
43489         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43490             this.inputEl().on("keypress", this.filterKeys, this);
43491         } else {
43492             this.inputEl().relayEvent('keypress', this);
43493         }
43494         
43495         var allowed = "0123456789";
43496         
43497         if(this.allowDecimals){
43498             allowed += this.decimalSeparator;
43499         }
43500         
43501         if(this.allowNegative){
43502             allowed += "-";
43503         }
43504         
43505         if(this.thousandsDelimiter) {
43506             allowed += ",";
43507         }
43508         
43509         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43510         
43511         var keyPress = function(e){
43512             
43513             var k = e.getKey();
43514             
43515             var c = e.getCharCode();
43516             
43517             if(
43518                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43519                     allowed.indexOf(String.fromCharCode(c)) === -1
43520             ){
43521                 e.stopEvent();
43522                 return;
43523             }
43524             
43525             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43526                 return;
43527             }
43528             
43529             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43530                 e.stopEvent();
43531             }
43532         };
43533         
43534         this.inputEl().on("keypress", keyPress, this);
43535         
43536     },
43537     
43538     onTriggerClick : function(e)
43539     {   
43540         if(this.disabled){
43541             return;
43542         }
43543         
43544         this.page = 0;
43545         this.loadNext = false;
43546         
43547         if(this.isExpanded()){
43548             this.collapse();
43549             return;
43550         }
43551         
43552         this.hasFocus = true;
43553         
43554         if(this.triggerAction == 'all') {
43555             this.doQuery(this.allQuery, true);
43556             return;
43557         }
43558         
43559         this.doQuery(this.getRawValue());
43560     },
43561     
43562     getCurrency : function()
43563     {   
43564         var v = this.currencyEl().getValue();
43565         
43566         return v;
43567     },
43568     
43569     restrictHeight : function()
43570     {
43571         this.list.alignTo(this.currencyEl(), this.listAlign);
43572         this.list.alignTo(this.currencyEl(), this.listAlign);
43573     },
43574     
43575     onViewClick : function(view, doFocus, el, e)
43576     {
43577         var index = this.view.getSelectedIndexes()[0];
43578         
43579         var r = this.store.getAt(index);
43580         
43581         if(r){
43582             this.onSelect(r, index);
43583         }
43584     },
43585     
43586     onSelect : function(record, index){
43587         
43588         if(this.fireEvent('beforeselect', this, record, index) !== false){
43589         
43590             this.setFromCurrencyData(index > -1 ? record.data : false);
43591             
43592             this.collapse();
43593             
43594             this.fireEvent('select', this, record, index);
43595         }
43596     },
43597     
43598     setFromCurrencyData : function(o)
43599     {
43600         var currency = '';
43601         
43602         this.lastCurrency = o;
43603         
43604         if (this.currencyField) {
43605             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43606         } else {
43607             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43608         }
43609         
43610         this.lastSelectionText = currency;
43611         
43612         //setting default currency
43613         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43614             this.setCurrency(this.defaultCurrency);
43615             return;
43616         }
43617         
43618         this.setCurrency(currency);
43619     },
43620     
43621     setFromData : function(o)
43622     {
43623         var c = {};
43624         
43625         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43626         
43627         this.setFromCurrencyData(c);
43628         
43629         var value = '';
43630         
43631         if (this.name) {
43632             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43633         } else {
43634             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43635         }
43636         
43637         this.setValue(value);
43638         
43639     },
43640     
43641     setCurrency : function(v)
43642     {   
43643         this.currencyValue = v;
43644         
43645         if(this.rendered){
43646             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43647             this.validate();
43648         }
43649     },
43650     
43651     setValue : function(v)
43652     {
43653         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43654         
43655         this.value = v;
43656         
43657         if(this.rendered){
43658             
43659             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43660             
43661             this.inputEl().dom.value = (v == '') ? '' :
43662                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43663             
43664             if(!this.allowZero && v === '0') {
43665                 this.hiddenEl().dom.value = '';
43666                 this.inputEl().dom.value = '';
43667             }
43668             
43669             this.validate();
43670         }
43671     },
43672     
43673     getRawValue : function()
43674     {
43675         var v = this.inputEl().getValue();
43676         
43677         return v;
43678     },
43679     
43680     getValue : function()
43681     {
43682         return this.fixPrecision(this.parseValue(this.getRawValue()));
43683     },
43684     
43685     parseValue : function(value)
43686     {
43687         if(this.thousandsDelimiter) {
43688             value += "";
43689             r = new RegExp(",", "g");
43690             value = value.replace(r, "");
43691         }
43692         
43693         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43694         return isNaN(value) ? '' : value;
43695         
43696     },
43697     
43698     fixPrecision : function(value)
43699     {
43700         if(this.thousandsDelimiter) {
43701             value += "";
43702             r = new RegExp(",", "g");
43703             value = value.replace(r, "");
43704         }
43705         
43706         var nan = isNaN(value);
43707         
43708         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43709             return nan ? '' : value;
43710         }
43711         return parseFloat(value).toFixed(this.decimalPrecision);
43712     },
43713     
43714     decimalPrecisionFcn : function(v)
43715     {
43716         return Math.floor(v);
43717     },
43718     
43719     validateValue : function(value)
43720     {
43721         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43722             return false;
43723         }
43724         
43725         var num = this.parseValue(value);
43726         
43727         if(isNaN(num)){
43728             this.markInvalid(String.format(this.nanText, value));
43729             return false;
43730         }
43731         
43732         if(num < this.minValue){
43733             this.markInvalid(String.format(this.minText, this.minValue));
43734             return false;
43735         }
43736         
43737         if(num > this.maxValue){
43738             this.markInvalid(String.format(this.maxText, this.maxValue));
43739             return false;
43740         }
43741         
43742         return true;
43743     },
43744     
43745     validate : function()
43746     {
43747         if(this.disabled || this.allowBlank){
43748             this.markValid();
43749             return true;
43750         }
43751         
43752         var currency = this.getCurrency();
43753         
43754         if(this.validateValue(this.getRawValue()) && currency.length){
43755             this.markValid();
43756             return true;
43757         }
43758         
43759         this.markInvalid();
43760         return false;
43761     },
43762     
43763     getName: function()
43764     {
43765         return this.name;
43766     },
43767     
43768     beforeBlur : function()
43769     {
43770         if(!this.castInt){
43771             return;
43772         }
43773         
43774         var v = this.parseValue(this.getRawValue());
43775         
43776         if(v || v == 0){
43777             this.setValue(v);
43778         }
43779     },
43780     
43781     onBlur : function()
43782     {
43783         this.beforeBlur();
43784         
43785         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43786             //this.el.removeClass(this.focusClass);
43787         }
43788         
43789         this.hasFocus = false;
43790         
43791         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43792             this.validate();
43793         }
43794         
43795         var v = this.getValue();
43796         
43797         if(String(v) !== String(this.startValue)){
43798             this.fireEvent('change', this, v, this.startValue);
43799         }
43800         
43801         this.fireEvent("blur", this);
43802     },
43803     
43804     inputEl : function()
43805     {
43806         return this.el.select('.roo-money-amount-input', true).first();
43807     },
43808     
43809     currencyEl : function()
43810     {
43811         return this.el.select('.roo-money-currency-input', true).first();
43812     },
43813     
43814     hiddenEl : function()
43815     {
43816         return this.el.select('input.hidden-number-input',true).first();
43817     }
43818     
43819 });/**
43820  * @class Roo.bootstrap.BezierSignature
43821  * @extends Roo.bootstrap.Component
43822  * Bootstrap BezierSignature class
43823  * This script refer to:
43824  *    Title: Signature Pad
43825  *    Author: szimek
43826  *    Availability: https://github.com/szimek/signature_pad
43827  *
43828  * @constructor
43829  * Create a new BezierSignature
43830  * @param {Object} config The config object
43831  */
43832
43833 Roo.bootstrap.BezierSignature = function(config){
43834     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43835     this.addEvents({
43836         "resize" : true
43837     });
43838 };
43839
43840 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43841 {
43842      
43843     curve_data: [],
43844     
43845     is_empty: true,
43846     
43847     mouse_btn_down: true,
43848     
43849     /**
43850      * @cfg {int} canvas height
43851      */
43852     canvas_height: '200px',
43853     
43854     /**
43855      * @cfg {float|function} Radius of a single dot.
43856      */ 
43857     dot_size: false,
43858     
43859     /**
43860      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43861      */
43862     min_width: 0.5,
43863     
43864     /**
43865      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43866      */
43867     max_width: 2.5,
43868     
43869     /**
43870      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43871      */
43872     throttle: 16,
43873     
43874     /**
43875      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43876      */
43877     min_distance: 5,
43878     
43879     /**
43880      * @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.
43881      */
43882     bg_color: 'rgba(0, 0, 0, 0)',
43883     
43884     /**
43885      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43886      */
43887     dot_color: 'black',
43888     
43889     /**
43890      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43891      */ 
43892     velocity_filter_weight: 0.7,
43893     
43894     /**
43895      * @cfg {function} Callback when stroke begin. 
43896      */
43897     onBegin: false,
43898     
43899     /**
43900      * @cfg {function} Callback when stroke end.
43901      */
43902     onEnd: false,
43903     
43904     getAutoCreate : function()
43905     {
43906         var cls = 'roo-signature column';
43907         
43908         if(this.cls){
43909             cls += ' ' + this.cls;
43910         }
43911         
43912         var col_sizes = [
43913             'lg',
43914             'md',
43915             'sm',
43916             'xs'
43917         ];
43918         
43919         for(var i = 0; i < col_sizes.length; i++) {
43920             if(this[col_sizes[i]]) {
43921                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43922             }
43923         }
43924         
43925         var cfg = {
43926             tag: 'div',
43927             cls: cls,
43928             cn: [
43929                 {
43930                     tag: 'div',
43931                     cls: 'roo-signature-body',
43932                     cn: [
43933                         {
43934                             tag: 'canvas',
43935                             cls: 'roo-signature-body-canvas',
43936                             height: this.canvas_height,
43937                             width: this.canvas_width
43938                         }
43939                     ]
43940                 },
43941                 {
43942                     tag: 'input',
43943                     type: 'file',
43944                     style: 'display: none'
43945                 }
43946             ]
43947         };
43948         
43949         return cfg;
43950     },
43951     
43952     initEvents: function() 
43953     {
43954         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43955         
43956         var canvas = this.canvasEl();
43957         
43958         // mouse && touch event swapping...
43959         canvas.dom.style.touchAction = 'none';
43960         canvas.dom.style.msTouchAction = 'none';
43961         
43962         this.mouse_btn_down = false;
43963         canvas.on('mousedown', this._handleMouseDown, this);
43964         canvas.on('mousemove', this._handleMouseMove, this);
43965         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43966         
43967         if (window.PointerEvent) {
43968             canvas.on('pointerdown', this._handleMouseDown, this);
43969             canvas.on('pointermove', this._handleMouseMove, this);
43970             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43971         }
43972         
43973         if ('ontouchstart' in window) {
43974             canvas.on('touchstart', this._handleTouchStart, this);
43975             canvas.on('touchmove', this._handleTouchMove, this);
43976             canvas.on('touchend', this._handleTouchEnd, this);
43977         }
43978         
43979         Roo.EventManager.onWindowResize(this.resize, this, true);
43980         
43981         // file input event
43982         this.fileEl().on('change', this.uploadImage, this);
43983         
43984         this.clear();
43985         
43986         this.resize();
43987     },
43988     
43989     resize: function(){
43990         
43991         var canvas = this.canvasEl().dom;
43992         var ctx = this.canvasElCtx();
43993         var img_data = false;
43994         
43995         if(canvas.width > 0) {
43996             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43997         }
43998         // setting canvas width will clean img data
43999         canvas.width = 0;
44000         
44001         var style = window.getComputedStyle ? 
44002             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44003             
44004         var padding_left = parseInt(style.paddingLeft) || 0;
44005         var padding_right = parseInt(style.paddingRight) || 0;
44006         
44007         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44008         
44009         if(img_data) {
44010             ctx.putImageData(img_data, 0, 0);
44011         }
44012     },
44013     
44014     _handleMouseDown: function(e)
44015     {
44016         if (e.browserEvent.which === 1) {
44017             this.mouse_btn_down = true;
44018             this.strokeBegin(e);
44019         }
44020     },
44021     
44022     _handleMouseMove: function (e)
44023     {
44024         if (this.mouse_btn_down) {
44025             this.strokeMoveUpdate(e);
44026         }
44027     },
44028     
44029     _handleMouseUp: function (e)
44030     {
44031         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44032             this.mouse_btn_down = false;
44033             this.strokeEnd(e);
44034         }
44035     },
44036     
44037     _handleTouchStart: function (e) {
44038         
44039         e.preventDefault();
44040         if (e.browserEvent.targetTouches.length === 1) {
44041             // var touch = e.browserEvent.changedTouches[0];
44042             // this.strokeBegin(touch);
44043             
44044              this.strokeBegin(e); // assume e catching the correct xy...
44045         }
44046     },
44047     
44048     _handleTouchMove: function (e) {
44049         e.preventDefault();
44050         // var touch = event.targetTouches[0];
44051         // _this._strokeMoveUpdate(touch);
44052         this.strokeMoveUpdate(e);
44053     },
44054     
44055     _handleTouchEnd: function (e) {
44056         var wasCanvasTouched = e.target === this.canvasEl().dom;
44057         if (wasCanvasTouched) {
44058             e.preventDefault();
44059             // var touch = event.changedTouches[0];
44060             // _this._strokeEnd(touch);
44061             this.strokeEnd(e);
44062         }
44063     },
44064     
44065     reset: function () {
44066         this._lastPoints = [];
44067         this._lastVelocity = 0;
44068         this._lastWidth = (this.min_width + this.max_width) / 2;
44069         this.canvasElCtx().fillStyle = this.dot_color;
44070     },
44071     
44072     strokeMoveUpdate: function(e)
44073     {
44074         this.strokeUpdate(e);
44075         
44076         if (this.throttle) {
44077             this.throttleStroke(this.strokeUpdate, this.throttle);
44078         }
44079         else {
44080             this.strokeUpdate(e);
44081         }
44082     },
44083     
44084     strokeBegin: function(e)
44085     {
44086         var newPointGroup = {
44087             color: this.dot_color,
44088             points: []
44089         };
44090         
44091         if (typeof this.onBegin === 'function') {
44092             this.onBegin(e);
44093         }
44094         
44095         this.curve_data.push(newPointGroup);
44096         this.reset();
44097         this.strokeUpdate(e);
44098     },
44099     
44100     strokeUpdate: function(e)
44101     {
44102         var rect = this.canvasEl().dom.getBoundingClientRect();
44103         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44104         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44105         var lastPoints = lastPointGroup.points;
44106         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44107         var isLastPointTooClose = lastPoint
44108             ? point.distanceTo(lastPoint) <= this.min_distance
44109             : false;
44110         var color = lastPointGroup.color;
44111         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44112             var curve = this.addPoint(point);
44113             if (!lastPoint) {
44114                 this.drawDot({color: color, point: point});
44115             }
44116             else if (curve) {
44117                 this.drawCurve({color: color, curve: curve});
44118             }
44119             lastPoints.push({
44120                 time: point.time,
44121                 x: point.x,
44122                 y: point.y
44123             });
44124         }
44125     },
44126     
44127     strokeEnd: function(e)
44128     {
44129         this.strokeUpdate(e);
44130         if (typeof this.onEnd === 'function') {
44131             this.onEnd(e);
44132         }
44133     },
44134     
44135     addPoint:  function (point) {
44136         var _lastPoints = this._lastPoints;
44137         _lastPoints.push(point);
44138         if (_lastPoints.length > 2) {
44139             if (_lastPoints.length === 3) {
44140                 _lastPoints.unshift(_lastPoints[0]);
44141             }
44142             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44143             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44144             _lastPoints.shift();
44145             return curve;
44146         }
44147         return null;
44148     },
44149     
44150     calculateCurveWidths: function (startPoint, endPoint) {
44151         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44152             (1 - this.velocity_filter_weight) * this._lastVelocity;
44153
44154         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44155         var widths = {
44156             end: newWidth,
44157             start: this._lastWidth
44158         };
44159         
44160         this._lastVelocity = velocity;
44161         this._lastWidth = newWidth;
44162         return widths;
44163     },
44164     
44165     drawDot: function (_a) {
44166         var color = _a.color, point = _a.point;
44167         var ctx = this.canvasElCtx();
44168         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44169         ctx.beginPath();
44170         this.drawCurveSegment(point.x, point.y, width);
44171         ctx.closePath();
44172         ctx.fillStyle = color;
44173         ctx.fill();
44174     },
44175     
44176     drawCurve: function (_a) {
44177         var color = _a.color, curve = _a.curve;
44178         var ctx = this.canvasElCtx();
44179         var widthDelta = curve.endWidth - curve.startWidth;
44180         var drawSteps = Math.floor(curve.length()) * 2;
44181         ctx.beginPath();
44182         ctx.fillStyle = color;
44183         for (var i = 0; i < drawSteps; i += 1) {
44184         var t = i / drawSteps;
44185         var tt = t * t;
44186         var ttt = tt * t;
44187         var u = 1 - t;
44188         var uu = u * u;
44189         var uuu = uu * u;
44190         var x = uuu * curve.startPoint.x;
44191         x += 3 * uu * t * curve.control1.x;
44192         x += 3 * u * tt * curve.control2.x;
44193         x += ttt * curve.endPoint.x;
44194         var y = uuu * curve.startPoint.y;
44195         y += 3 * uu * t * curve.control1.y;
44196         y += 3 * u * tt * curve.control2.y;
44197         y += ttt * curve.endPoint.y;
44198         var width = curve.startWidth + ttt * widthDelta;
44199         this.drawCurveSegment(x, y, width);
44200         }
44201         ctx.closePath();
44202         ctx.fill();
44203     },
44204     
44205     drawCurveSegment: function (x, y, width) {
44206         var ctx = this.canvasElCtx();
44207         ctx.moveTo(x, y);
44208         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44209         this.is_empty = false;
44210     },
44211     
44212     clear: function()
44213     {
44214         var ctx = this.canvasElCtx();
44215         var canvas = this.canvasEl().dom;
44216         ctx.fillStyle = this.bg_color;
44217         ctx.clearRect(0, 0, canvas.width, canvas.height);
44218         ctx.fillRect(0, 0, canvas.width, canvas.height);
44219         this.curve_data = [];
44220         this.reset();
44221         this.is_empty = true;
44222     },
44223     
44224     fileEl: function()
44225     {
44226         return  this.el.select('input',true).first();
44227     },
44228     
44229     canvasEl: function()
44230     {
44231         return this.el.select('canvas',true).first();
44232     },
44233     
44234     canvasElCtx: function()
44235     {
44236         return this.el.select('canvas',true).first().dom.getContext('2d');
44237     },
44238     
44239     getImage: function(type)
44240     {
44241         if(this.is_empty) {
44242             return false;
44243         }
44244         
44245         // encryption ?
44246         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44247     },
44248     
44249     drawFromImage: function(img_src)
44250     {
44251         var img = new Image();
44252         
44253         img.onload = function(){
44254             this.canvasElCtx().drawImage(img, 0, 0);
44255         }.bind(this);
44256         
44257         img.src = img_src;
44258         
44259         this.is_empty = false;
44260     },
44261     
44262     selectImage: function()
44263     {
44264         this.fileEl().dom.click();
44265     },
44266     
44267     uploadImage: function(e)
44268     {
44269         var reader = new FileReader();
44270         
44271         reader.onload = function(e){
44272             var img = new Image();
44273             img.onload = function(){
44274                 this.reset();
44275                 this.canvasElCtx().drawImage(img, 0, 0);
44276             }.bind(this);
44277             img.src = e.target.result;
44278         }.bind(this);
44279         
44280         reader.readAsDataURL(e.target.files[0]);
44281     },
44282     
44283     // Bezier Point Constructor
44284     Point: (function () {
44285         function Point(x, y, time) {
44286             this.x = x;
44287             this.y = y;
44288             this.time = time || Date.now();
44289         }
44290         Point.prototype.distanceTo = function (start) {
44291             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44292         };
44293         Point.prototype.equals = function (other) {
44294             return this.x === other.x && this.y === other.y && this.time === other.time;
44295         };
44296         Point.prototype.velocityFrom = function (start) {
44297             return this.time !== start.time
44298             ? this.distanceTo(start) / (this.time - start.time)
44299             : 0;
44300         };
44301         return Point;
44302     }()),
44303     
44304     
44305     // Bezier Constructor
44306     Bezier: (function () {
44307         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44308             this.startPoint = startPoint;
44309             this.control2 = control2;
44310             this.control1 = control1;
44311             this.endPoint = endPoint;
44312             this.startWidth = startWidth;
44313             this.endWidth = endWidth;
44314         }
44315         Bezier.fromPoints = function (points, widths, scope) {
44316             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44317             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44318             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44319         };
44320         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44321             var dx1 = s1.x - s2.x;
44322             var dy1 = s1.y - s2.y;
44323             var dx2 = s2.x - s3.x;
44324             var dy2 = s2.y - s3.y;
44325             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44326             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44327             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44328             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44329             var dxm = m1.x - m2.x;
44330             var dym = m1.y - m2.y;
44331             var k = l2 / (l1 + l2);
44332             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44333             var tx = s2.x - cm.x;
44334             var ty = s2.y - cm.y;
44335             return {
44336                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44337                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44338             };
44339         };
44340         Bezier.prototype.length = function () {
44341             var steps = 10;
44342             var length = 0;
44343             var px;
44344             var py;
44345             for (var i = 0; i <= steps; i += 1) {
44346                 var t = i / steps;
44347                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44348                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44349                 if (i > 0) {
44350                     var xdiff = cx - px;
44351                     var ydiff = cy - py;
44352                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44353                 }
44354                 px = cx;
44355                 py = cy;
44356             }
44357             return length;
44358         };
44359         Bezier.prototype.point = function (t, start, c1, c2, end) {
44360             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44361             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44362             + (3.0 * c2 * (1.0 - t) * t * t)
44363             + (end * t * t * t);
44364         };
44365         return Bezier;
44366     }()),
44367     
44368     throttleStroke: function(fn, wait) {
44369       if (wait === void 0) { wait = 250; }
44370       var previous = 0;
44371       var timeout = null;
44372       var result;
44373       var storedContext;
44374       var storedArgs;
44375       var later = function () {
44376           previous = Date.now();
44377           timeout = null;
44378           result = fn.apply(storedContext, storedArgs);
44379           if (!timeout) {
44380               storedContext = null;
44381               storedArgs = [];
44382           }
44383       };
44384       return function wrapper() {
44385           var args = [];
44386           for (var _i = 0; _i < arguments.length; _i++) {
44387               args[_i] = arguments[_i];
44388           }
44389           var now = Date.now();
44390           var remaining = wait - (now - previous);
44391           storedContext = this;
44392           storedArgs = args;
44393           if (remaining <= 0 || remaining > wait) {
44394               if (timeout) {
44395                   clearTimeout(timeout);
44396                   timeout = null;
44397               }
44398               previous = now;
44399               result = fn.apply(storedContext, storedArgs);
44400               if (!timeout) {
44401                   storedContext = null;
44402                   storedArgs = [];
44403               }
44404           }
44405           else if (!timeout) {
44406               timeout = window.setTimeout(later, remaining);
44407           }
44408           return result;
44409       };
44410   }
44411   
44412 });
44413
44414  
44415
44416