sync
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
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 {Number} width fixed width - usefull for chrome extension only really.
3951  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3952  * @cfg {String} size (sm|lg|xl) default empty
3953  * @cfg {Number} max_width set the max width of modal
3954  * @cfg {Boolean} editableTitle can the title be edited
3955
3956  *
3957  *
3958  * @constructor
3959  * Create a new Modal Dialog
3960  * @param {Object} config The config object
3961  */
3962
3963 Roo.bootstrap.Modal = function(config){
3964     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3965     this.addEvents({
3966         // raw events
3967         /**
3968          * @event btnclick
3969          * The raw btnclick event for the button
3970          * @param {Roo.EventObject} e
3971          */
3972         "btnclick" : true,
3973         /**
3974          * @event resize
3975          * Fire when dialog resize
3976          * @param {Roo.bootstrap.Modal} this
3977          * @param {Roo.EventObject} e
3978          */
3979         "resize" : true,
3980         /**
3981          * @event titlechanged
3982          * Fire when the editable title has been changed
3983          * @param {Roo.bootstrap.Modal} this
3984          * @param {Roo.EventObject} value
3985          */
3986         "titlechanged" : true 
3987         
3988     });
3989     this.buttons = this.buttons || [];
3990
3991     if (this.tmpl) {
3992         this.tmpl = Roo.factory(this.tmpl);
3993     }
3994
3995 };
3996
3997 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3998
3999     title : 'test dialog',
4000
4001     buttons : false,
4002
4003     // set on load...
4004
4005     html: false,
4006
4007     tmp: false,
4008
4009     specificTitle: false,
4010
4011     buttonPosition: 'right',
4012
4013     allow_close : true,
4014
4015     animate : true,
4016
4017     fitwindow: false,
4018     
4019      // private
4020     dialogEl: false,
4021     bodyEl:  false,
4022     footerEl:  false,
4023     titleEl:  false,
4024     closeEl:  false,
4025
4026     size: '',
4027     
4028     max_width: 0,
4029     
4030     max_height: 0,
4031     
4032     fit_content: false,
4033     editableTitle  : false,
4034
4035     onRender : function(ct, position)
4036     {
4037         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4038
4039         if(!this.el){
4040             var cfg = Roo.apply({},  this.getAutoCreate());
4041             cfg.id = Roo.id();
4042             //if(!cfg.name){
4043             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4044             //}
4045             //if (!cfg.name.length) {
4046             //    delete cfg.name;
4047            // }
4048             if (this.cls) {
4049                 cfg.cls += ' ' + this.cls;
4050             }
4051             if (this.style) {
4052                 cfg.style = this.style;
4053             }
4054             this.el = Roo.get(document.body).createChild(cfg, position);
4055         }
4056         //var type = this.el.dom.type;
4057
4058
4059         if(this.tabIndex !== undefined){
4060             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4061         }
4062
4063         this.dialogEl = this.el.select('.modal-dialog',true).first();
4064         this.bodyEl = this.el.select('.modal-body',true).first();
4065         this.closeEl = this.el.select('.modal-header .close', true).first();
4066         this.headerEl = this.el.select('.modal-header',true).first();
4067         this.titleEl = this.el.select('.modal-title',true).first();
4068         this.footerEl = this.el.select('.modal-footer',true).first();
4069
4070         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4071         
4072         //this.el.addClass("x-dlg-modal");
4073
4074         if (this.buttons.length) {
4075             Roo.each(this.buttons, function(bb) {
4076                 var b = Roo.apply({}, bb);
4077                 b.xns = b.xns || Roo.bootstrap;
4078                 b.xtype = b.xtype || 'Button';
4079                 if (typeof(b.listeners) == 'undefined') {
4080                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4081                 }
4082
4083                 var btn = Roo.factory(b);
4084
4085                 btn.render(this.getButtonContainer());
4086
4087             },this);
4088         }
4089         // render the children.
4090         var nitems = [];
4091
4092         if(typeof(this.items) != 'undefined'){
4093             var items = this.items;
4094             delete this.items;
4095
4096             for(var i =0;i < items.length;i++) {
4097                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4098             }
4099         }
4100
4101         this.items = nitems;
4102
4103         // where are these used - they used to be body/close/footer
4104
4105
4106         this.initEvents();
4107         //this.el.addClass([this.fieldClass, this.cls]);
4108
4109     },
4110
4111     getAutoCreate : function()
4112     {
4113         // we will default to modal-body-overflow - might need to remove or make optional later.
4114         var bdy = {
4115                 cls : 'modal-body ' + (this.fitwindow ? 'overflow-auto' : ''), 
4116                 html : this.html || ''
4117         };
4118
4119         var title = {
4120             tag: 'h4',
4121             cls : 'modal-title',
4122             html : this.title
4123         };
4124
4125         if(this.specificTitle){ // WTF is this?
4126             title = this.title;
4127         }
4128
4129         var header = [];
4130         if (this.allow_close && Roo.bootstrap.version == 3) {
4131             header.push({
4132                 tag: 'button',
4133                 cls : 'close',
4134                 html : '&times'
4135             });
4136         }
4137
4138         header.push(title);
4139
4140         if (this.editableTitle) {
4141             header.push({
4142                 cls: 'form-control roo-editable-title d-none',
4143                 tag: 'input',
4144                 type: 'text'
4145             });
4146         }
4147         
4148         if (this.allow_close && Roo.bootstrap.version == 4) {
4149             header.push({
4150                 tag: 'button',
4151                 cls : 'close',
4152                 html : '&times'
4153             });
4154         }
4155         
4156         var size = '';
4157
4158         if(this.size.length){
4159             size = 'modal-' + this.size;
4160         }
4161         
4162         var footer = Roo.bootstrap.version == 3 ?
4163             {
4164                 cls : 'modal-footer',
4165                 cn : [
4166                     {
4167                         tag: 'div',
4168                         cls: 'btn-' + this.buttonPosition
4169                     }
4170                 ]
4171
4172             } :
4173             {  // BS4 uses mr-auto on left buttons....
4174                 cls : 'modal-footer'
4175             };
4176
4177             
4178
4179         
4180         
4181         var modal = {
4182             cls: "modal",
4183              cn : [
4184                 {
4185                     cls: "modal-dialog " + size,
4186                     cn : [
4187                         {
4188                             cls : "modal-content",
4189                             cn : [
4190                                 {
4191                                     cls : 'modal-header',
4192                                     cn : header
4193                                 },
4194                                 bdy,
4195                                 footer
4196                             ]
4197
4198                         }
4199                     ]
4200
4201                 }
4202             ]
4203         };
4204
4205         if(this.animate){
4206             modal.cls += ' fade';
4207         }
4208
4209         return modal;
4210
4211     },
4212     getChildContainer : function() {
4213
4214          return this.bodyEl;
4215
4216     },
4217     getButtonContainer : function() {
4218         
4219          return Roo.bootstrap.version == 4 ?
4220             this.el.select('.modal-footer',true).first()
4221             : this.el.select('.modal-footer div',true).first();
4222
4223     },
4224     initEvents : function()
4225     {
4226         if (this.allow_close) {
4227             this.closeEl.on('click', this.hide, this);
4228         }
4229         Roo.EventManager.onWindowResize(this.resize, this, true);
4230         if (this.editableTitle) {
4231             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4232             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4233             this.headerEditEl.on('keyup', function(e) {
4234                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4235                         this.toggleHeaderInput(false)
4236                     }
4237                 }, this);
4238             this.headerEditEl.on('blur', function(e) {
4239                 this.toggleHeaderInput(false)
4240             },this);
4241         }
4242
4243     },
4244   
4245
4246     resize : function()
4247     {
4248         this.maskEl.setSize(
4249             Roo.lib.Dom.getViewWidth(true),
4250             Roo.lib.Dom.getViewHeight(true)
4251         );
4252         
4253         if (this.fitwindow) {
4254             
4255            
4256             this.setSize(
4257                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4258                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4259             );
4260             return;
4261         }
4262         
4263         if(this.max_width !== 0) {
4264             
4265             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4266             
4267             if(this.height) {
4268                 this.setSize(w, this.height);
4269                 return;
4270             }
4271             
4272             if(this.max_height) {
4273                 this.setSize(w,Math.min(
4274                     this.max_height,
4275                     Roo.lib.Dom.getViewportHeight(true) - 60
4276                 ));
4277                 
4278                 return;
4279             }
4280             
4281             if(!this.fit_content) {
4282                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4283                 return;
4284             }
4285             
4286             this.setSize(w, Math.min(
4287                 60 +
4288                 this.headerEl.getHeight() + 
4289                 this.footerEl.getHeight() + 
4290                 this.getChildHeight(this.bodyEl.dom.childNodes),
4291                 Roo.lib.Dom.getViewportHeight(true) - 60)
4292             );
4293         }
4294         
4295     },
4296
4297     setSize : function(w,h)
4298     {
4299         if (!w && !h) {
4300             return;
4301         }
4302         
4303         this.resizeTo(w,h);
4304     },
4305
4306     show : function() {
4307
4308         if (!this.rendered) {
4309             this.render();
4310         }
4311         this.toggleHeaderInput(false);
4312         //this.el.setStyle('display', 'block');
4313         this.el.removeClass('hideing');
4314         this.el.dom.style.display='block';
4315         
4316         Roo.get(document.body).addClass('modal-open');
4317  
4318         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4319             
4320             (function(){
4321                 this.el.addClass('show');
4322                 this.el.addClass('in');
4323             }).defer(50, this);
4324         }else{
4325             this.el.addClass('show');
4326             this.el.addClass('in');
4327         }
4328
4329         // not sure how we can show data in here..
4330         //if (this.tmpl) {
4331         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4332         //}
4333
4334         Roo.get(document.body).addClass("x-body-masked");
4335         
4336         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4337         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4338         this.maskEl.dom.style.display = 'block';
4339         this.maskEl.addClass('show');
4340         
4341         
4342         this.resize();
4343         
4344         this.fireEvent('show', this);
4345
4346         // set zindex here - otherwise it appears to be ignored...
4347         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4348
4349         (function () {
4350             this.items.forEach( function(e) {
4351                 e.layout ? e.layout() : false;
4352
4353             });
4354         }).defer(100,this);
4355
4356     },
4357     hide : function()
4358     {
4359         if(this.fireEvent("beforehide", this) !== false){
4360             
4361             this.maskEl.removeClass('show');
4362             
4363             this.maskEl.dom.style.display = '';
4364             Roo.get(document.body).removeClass("x-body-masked");
4365             this.el.removeClass('in');
4366             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4367
4368             if(this.animate){ // why
4369                 this.el.addClass('hideing');
4370                 this.el.removeClass('show');
4371                 (function(){
4372                     if (!this.el.hasClass('hideing')) {
4373                         return; // it's been shown again...
4374                     }
4375                     
4376                     this.el.dom.style.display='';
4377
4378                     Roo.get(document.body).removeClass('modal-open');
4379                     this.el.removeClass('hideing');
4380                 }).defer(150,this);
4381                 
4382             }else{
4383                 this.el.removeClass('show');
4384                 this.el.dom.style.display='';
4385                 Roo.get(document.body).removeClass('modal-open');
4386
4387             }
4388             this.fireEvent('hide', this);
4389         }
4390     },
4391     isVisible : function()
4392     {
4393         
4394         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4395         
4396     },
4397
4398     addButton : function(str, cb)
4399     {
4400
4401
4402         var b = Roo.apply({}, { html : str } );
4403         b.xns = b.xns || Roo.bootstrap;
4404         b.xtype = b.xtype || 'Button';
4405         if (typeof(b.listeners) == 'undefined') {
4406             b.listeners = { click : cb.createDelegate(this)  };
4407         }
4408
4409         var btn = Roo.factory(b);
4410
4411         btn.render(this.getButtonContainer());
4412
4413         return btn;
4414
4415     },
4416
4417     setDefaultButton : function(btn)
4418     {
4419         //this.el.select('.modal-footer').()
4420     },
4421
4422     resizeTo: function(w,h)
4423     {
4424         this.dialogEl.setWidth(w);
4425         
4426         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4427
4428         this.bodyEl.setHeight(h - diff);
4429         
4430         this.fireEvent('resize', this);
4431     },
4432     
4433     setContentSize  : function(w, h)
4434     {
4435
4436     },
4437     onButtonClick: function(btn,e)
4438     {
4439         //Roo.log([a,b,c]);
4440         this.fireEvent('btnclick', btn.name, e);
4441     },
4442      /**
4443      * Set the title of the Dialog
4444      * @param {String} str new Title
4445      */
4446     setTitle: function(str) {
4447         this.titleEl.dom.innerHTML = str;
4448         this.title = str;
4449     },
4450     /**
4451      * Set the body of the Dialog
4452      * @param {String} str new Title
4453      */
4454     setBody: function(str) {
4455         this.bodyEl.dom.innerHTML = str;
4456     },
4457     /**
4458      * Set the body of the Dialog using the template
4459      * @param {Obj} data - apply this data to the template and replace the body contents.
4460      */
4461     applyBody: function(obj)
4462     {
4463         if (!this.tmpl) {
4464             Roo.log("Error - using apply Body without a template");
4465             //code
4466         }
4467         this.tmpl.overwrite(this.bodyEl, obj);
4468     },
4469     
4470     getChildHeight : function(child_nodes)
4471     {
4472         if(
4473             !child_nodes ||
4474             child_nodes.length == 0
4475         ) {
4476             return 0;
4477         }
4478         
4479         var child_height = 0;
4480         
4481         for(var i = 0; i < child_nodes.length; i++) {
4482             
4483             /*
4484             * for modal with tabs...
4485             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4486                 
4487                 var layout_childs = child_nodes[i].childNodes;
4488                 
4489                 for(var j = 0; j < layout_childs.length; j++) {
4490                     
4491                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4492                         
4493                         var layout_body_childs = layout_childs[j].childNodes;
4494                         
4495                         for(var k = 0; k < layout_body_childs.length; k++) {
4496                             
4497                             if(layout_body_childs[k].classList.contains('navbar')) {
4498                                 child_height += layout_body_childs[k].offsetHeight;
4499                                 continue;
4500                             }
4501                             
4502                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4503                                 
4504                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4505                                 
4506                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4507                                     
4508                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4509                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4510                                         continue;
4511                                     }
4512                                     
4513                                 }
4514                                 
4515                             }
4516                             
4517                         }
4518                     }
4519                 }
4520                 continue;
4521             }
4522             */
4523             
4524             child_height += child_nodes[i].offsetHeight;
4525             // Roo.log(child_nodes[i].offsetHeight);
4526         }
4527         
4528         return child_height;
4529     },
4530     toggleHeaderInput : function(is_edit)
4531     {
4532         if (!this.editableTitle) {
4533             return; // not editable.
4534         }
4535         if (is_edit && this.is_header_editing) {
4536             return; // already editing..
4537         }
4538         if (is_edit) {
4539     
4540             this.headerEditEl.dom.value = this.title;
4541             this.headerEditEl.removeClass('d-none');
4542             this.headerEditEl.dom.focus();
4543             this.titleEl.addClass('d-none');
4544             
4545             this.is_header_editing = true;
4546             return
4547         }
4548         // flip back to not editing.
4549         this.title = this.headerEditEl.dom.value;
4550         this.headerEditEl.addClass('d-none');
4551         this.titleEl.removeClass('d-none');
4552         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4553         this.is_header_editing = false;
4554         this.fireEvent('titlechanged', this, this.title);
4555     
4556             
4557         
4558     }
4559
4560 });
4561
4562
4563 Roo.apply(Roo.bootstrap.Modal,  {
4564     /**
4565          * Button config that displays a single OK button
4566          * @type Object
4567          */
4568         OK :  [{
4569             name : 'ok',
4570             weight : 'primary',
4571             html : 'OK'
4572         }],
4573         /**
4574          * Button config that displays Yes and No buttons
4575          * @type Object
4576          */
4577         YESNO : [
4578             {
4579                 name  : 'no',
4580                 html : 'No'
4581             },
4582             {
4583                 name  :'yes',
4584                 weight : 'primary',
4585                 html : 'Yes'
4586             }
4587         ],
4588
4589         /**
4590          * Button config that displays OK and Cancel buttons
4591          * @type Object
4592          */
4593         OKCANCEL : [
4594             {
4595                name : 'cancel',
4596                 html : 'Cancel'
4597             },
4598             {
4599                 name : 'ok',
4600                 weight : 'primary',
4601                 html : 'OK'
4602             }
4603         ],
4604         /**
4605          * Button config that displays Yes, No and Cancel buttons
4606          * @type Object
4607          */
4608         YESNOCANCEL : [
4609             {
4610                 name : 'yes',
4611                 weight : 'primary',
4612                 html : 'Yes'
4613             },
4614             {
4615                 name : 'no',
4616                 html : 'No'
4617             },
4618             {
4619                 name : 'cancel',
4620                 html : 'Cancel'
4621             }
4622         ],
4623         
4624         zIndex : 10001
4625 });
4626
4627 /*
4628  * - LGPL
4629  *
4630  * messagebox - can be used as a replace
4631  * 
4632  */
4633 /**
4634  * @class Roo.MessageBox
4635  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4636  * Example usage:
4637  *<pre><code>
4638 // Basic alert:
4639 Roo.Msg.alert('Status', 'Changes saved successfully.');
4640
4641 // Prompt for user data:
4642 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4643     if (btn == 'ok'){
4644         // process text value...
4645     }
4646 });
4647
4648 // Show a dialog using config options:
4649 Roo.Msg.show({
4650    title:'Save Changes?',
4651    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4652    buttons: Roo.Msg.YESNOCANCEL,
4653    fn: processResult,
4654    animEl: 'elId'
4655 });
4656 </code></pre>
4657  * @singleton
4658  */
4659 Roo.bootstrap.MessageBox = function(){
4660     var dlg, opt, mask, waitTimer;
4661     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4662     var buttons, activeTextEl, bwidth;
4663
4664     
4665     // private
4666     var handleButton = function(button){
4667         dlg.hide();
4668         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4669     };
4670
4671     // private
4672     var handleHide = function(){
4673         if(opt && opt.cls){
4674             dlg.el.removeClass(opt.cls);
4675         }
4676         //if(waitTimer){
4677         //    Roo.TaskMgr.stop(waitTimer);
4678         //    waitTimer = null;
4679         //}
4680     };
4681
4682     // private
4683     var updateButtons = function(b){
4684         var width = 0;
4685         if(!b){
4686             buttons["ok"].hide();
4687             buttons["cancel"].hide();
4688             buttons["yes"].hide();
4689             buttons["no"].hide();
4690             dlg.footerEl.hide();
4691             
4692             return width;
4693         }
4694         dlg.footerEl.show();
4695         for(var k in buttons){
4696             if(typeof buttons[k] != "function"){
4697                 if(b[k]){
4698                     buttons[k].show();
4699                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4700                     width += buttons[k].el.getWidth()+15;
4701                 }else{
4702                     buttons[k].hide();
4703                 }
4704             }
4705         }
4706         return width;
4707     };
4708
4709     // private
4710     var handleEsc = function(d, k, e){
4711         if(opt && opt.closable !== false){
4712             dlg.hide();
4713         }
4714         if(e){
4715             e.stopEvent();
4716         }
4717     };
4718
4719     return {
4720         /**
4721          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4722          * @return {Roo.BasicDialog} The BasicDialog element
4723          */
4724         getDialog : function(){
4725            if(!dlg){
4726                 dlg = new Roo.bootstrap.Modal( {
4727                     //draggable: true,
4728                     //resizable:false,
4729                     //constraintoviewport:false,
4730                     //fixedcenter:true,
4731                     //collapsible : false,
4732                     //shim:true,
4733                     //modal: true,
4734                 //    width: 'auto',
4735                   //  height:100,
4736                     //buttonAlign:"center",
4737                     closeClick : function(){
4738                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4739                             handleButton("no");
4740                         }else{
4741                             handleButton("cancel");
4742                         }
4743                     }
4744                 });
4745                 dlg.render();
4746                 dlg.on("hide", handleHide);
4747                 mask = dlg.mask;
4748                 //dlg.addKeyListener(27, handleEsc);
4749                 buttons = {};
4750                 this.buttons = buttons;
4751                 var bt = this.buttonText;
4752                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4753                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4754                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4755                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4756                 //Roo.log(buttons);
4757                 bodyEl = dlg.bodyEl.createChild({
4758
4759                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4760                         '<textarea class="roo-mb-textarea"></textarea>' +
4761                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4762                 });
4763                 msgEl = bodyEl.dom.firstChild;
4764                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4765                 textboxEl.enableDisplayMode();
4766                 textboxEl.addKeyListener([10,13], function(){
4767                     if(dlg.isVisible() && opt && opt.buttons){
4768                         if(opt.buttons.ok){
4769                             handleButton("ok");
4770                         }else if(opt.buttons.yes){
4771                             handleButton("yes");
4772                         }
4773                     }
4774                 });
4775                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4776                 textareaEl.enableDisplayMode();
4777                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4778                 progressEl.enableDisplayMode();
4779                 
4780                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4781                 var pf = progressEl.dom.firstChild;
4782                 if (pf) {
4783                     pp = Roo.get(pf.firstChild);
4784                     pp.setHeight(pf.offsetHeight);
4785                 }
4786                 
4787             }
4788             return dlg;
4789         },
4790
4791         /**
4792          * Updates the message box body text
4793          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4794          * the XHTML-compliant non-breaking space character '&amp;#160;')
4795          * @return {Roo.MessageBox} This message box
4796          */
4797         updateText : function(text)
4798         {
4799             if(!dlg.isVisible() && !opt.width){
4800                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4801                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4802             }
4803             msgEl.innerHTML = text || '&#160;';
4804       
4805             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4806             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4807             var w = Math.max(
4808                     Math.min(opt.width || cw , this.maxWidth), 
4809                     Math.max(opt.minWidth || this.minWidth, bwidth)
4810             );
4811             if(opt.prompt){
4812                 activeTextEl.setWidth(w);
4813             }
4814             if(dlg.isVisible()){
4815                 dlg.fixedcenter = false;
4816             }
4817             // to big, make it scroll. = But as usual stupid IE does not support
4818             // !important..
4819             
4820             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4821                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4822                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4823             } else {
4824                 bodyEl.dom.style.height = '';
4825                 bodyEl.dom.style.overflowY = '';
4826             }
4827             if (cw > w) {
4828                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4829             } else {
4830                 bodyEl.dom.style.overflowX = '';
4831             }
4832             
4833             dlg.setContentSize(w, bodyEl.getHeight());
4834             if(dlg.isVisible()){
4835                 dlg.fixedcenter = true;
4836             }
4837             return this;
4838         },
4839
4840         /**
4841          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4842          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4843          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4844          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4845          * @return {Roo.MessageBox} This message box
4846          */
4847         updateProgress : function(value, text){
4848             if(text){
4849                 this.updateText(text);
4850             }
4851             
4852             if (pp) { // weird bug on my firefox - for some reason this is not defined
4853                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4854                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4855             }
4856             return this;
4857         },        
4858
4859         /**
4860          * Returns true if the message box is currently displayed
4861          * @return {Boolean} True if the message box is visible, else false
4862          */
4863         isVisible : function(){
4864             return dlg && dlg.isVisible();  
4865         },
4866
4867         /**
4868          * Hides the message box if it is displayed
4869          */
4870         hide : function(){
4871             if(this.isVisible()){
4872                 dlg.hide();
4873             }  
4874         },
4875
4876         /**
4877          * Displays a new message box, or reinitializes an existing message box, based on the config options
4878          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4879          * The following config object properties are supported:
4880          * <pre>
4881 Property    Type             Description
4882 ----------  ---------------  ------------------------------------------------------------------------------------
4883 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4884                                    closes (defaults to undefined)
4885 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4886                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4887 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4888                                    progress and wait dialogs will ignore this property and always hide the
4889                                    close button as they can only be closed programmatically.
4890 cls               String           A custom CSS class to apply to the message box element
4891 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4892                                    displayed (defaults to 75)
4893 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4894                                    function will be btn (the name of the button that was clicked, if applicable,
4895                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4896                                    Progress and wait dialogs will ignore this option since they do not respond to
4897                                    user actions and can only be closed programmatically, so any required function
4898                                    should be called by the same code after it closes the dialog.
4899 icon              String           A CSS class that provides a background image to be used as an icon for
4900                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4901 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4902 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4903 modal             Boolean          False to allow user interaction with the page while the message box is
4904                                    displayed (defaults to true)
4905 msg               String           A string that will replace the existing message box body text (defaults
4906                                    to the XHTML-compliant non-breaking space character '&#160;')
4907 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4908 progress          Boolean          True to display a progress bar (defaults to false)
4909 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4910 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4911 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4912 title             String           The title text
4913 value             String           The string value to set into the active textbox element if displayed
4914 wait              Boolean          True to display a progress bar (defaults to false)
4915 width             Number           The width of the dialog in pixels
4916 </pre>
4917          *
4918          * Example usage:
4919          * <pre><code>
4920 Roo.Msg.show({
4921    title: 'Address',
4922    msg: 'Please enter your address:',
4923    width: 300,
4924    buttons: Roo.MessageBox.OKCANCEL,
4925    multiline: true,
4926    fn: saveAddress,
4927    animEl: 'addAddressBtn'
4928 });
4929 </code></pre>
4930          * @param {Object} config Configuration options
4931          * @return {Roo.MessageBox} This message box
4932          */
4933         show : function(options)
4934         {
4935             
4936             // this causes nightmares if you show one dialog after another
4937             // especially on callbacks..
4938              
4939             if(this.isVisible()){
4940                 
4941                 this.hide();
4942                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4943                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4944                 Roo.log("New Dialog Message:" +  options.msg )
4945                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4946                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4947                 
4948             }
4949             var d = this.getDialog();
4950             opt = options;
4951             d.setTitle(opt.title || "&#160;");
4952             d.closeEl.setDisplayed(opt.closable !== false);
4953             activeTextEl = textboxEl;
4954             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4955             if(opt.prompt){
4956                 if(opt.multiline){
4957                     textboxEl.hide();
4958                     textareaEl.show();
4959                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4960                         opt.multiline : this.defaultTextHeight);
4961                     activeTextEl = textareaEl;
4962                 }else{
4963                     textboxEl.show();
4964                     textareaEl.hide();
4965                 }
4966             }else{
4967                 textboxEl.hide();
4968                 textareaEl.hide();
4969             }
4970             progressEl.setDisplayed(opt.progress === true);
4971             if (opt.progress) {
4972                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4973             }
4974             this.updateProgress(0);
4975             activeTextEl.dom.value = opt.value || "";
4976             if(opt.prompt){
4977                 dlg.setDefaultButton(activeTextEl);
4978             }else{
4979                 var bs = opt.buttons;
4980                 var db = null;
4981                 if(bs && bs.ok){
4982                     db = buttons["ok"];
4983                 }else if(bs && bs.yes){
4984                     db = buttons["yes"];
4985                 }
4986                 dlg.setDefaultButton(db);
4987             }
4988             bwidth = updateButtons(opt.buttons);
4989             this.updateText(opt.msg);
4990             if(opt.cls){
4991                 d.el.addClass(opt.cls);
4992             }
4993             d.proxyDrag = opt.proxyDrag === true;
4994             d.modal = opt.modal !== false;
4995             d.mask = opt.modal !== false ? mask : false;
4996             if(!d.isVisible()){
4997                 // force it to the end of the z-index stack so it gets a cursor in FF
4998                 document.body.appendChild(dlg.el.dom);
4999                 d.animateTarget = null;
5000                 d.show(options.animEl);
5001             }
5002             return this;
5003         },
5004
5005         /**
5006          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5007          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5008          * and closing the message box when the process is complete.
5009          * @param {String} title The title bar text
5010          * @param {String} msg The message box body text
5011          * @return {Roo.MessageBox} This message box
5012          */
5013         progress : function(title, msg){
5014             this.show({
5015                 title : title,
5016                 msg : msg,
5017                 buttons: false,
5018                 progress:true,
5019                 closable:false,
5020                 minWidth: this.minProgressWidth,
5021                 modal : true
5022             });
5023             return this;
5024         },
5025
5026         /**
5027          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5028          * If a callback function is passed it will be called after the user clicks the button, and the
5029          * id of the button that was clicked will be passed as the only parameter to the callback
5030          * (could also be the top-right close button).
5031          * @param {String} title The title bar text
5032          * @param {String} msg The message box body text
5033          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5034          * @param {Object} scope (optional) The scope of the callback function
5035          * @return {Roo.MessageBox} This message box
5036          */
5037         alert : function(title, msg, fn, scope)
5038         {
5039             this.show({
5040                 title : title,
5041                 msg : msg,
5042                 buttons: this.OK,
5043                 fn: fn,
5044                 closable : false,
5045                 scope : scope,
5046                 modal : true
5047             });
5048             return this;
5049         },
5050
5051         /**
5052          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5053          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5054          * You are responsible for closing the message box when the process is complete.
5055          * @param {String} msg The message box body text
5056          * @param {String} title (optional) The title bar text
5057          * @return {Roo.MessageBox} This message box
5058          */
5059         wait : function(msg, title){
5060             this.show({
5061                 title : title,
5062                 msg : msg,
5063                 buttons: false,
5064                 closable:false,
5065                 progress:true,
5066                 modal:true,
5067                 width:300,
5068                 wait:true
5069             });
5070             waitTimer = Roo.TaskMgr.start({
5071                 run: function(i){
5072                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5073                 },
5074                 interval: 1000
5075             });
5076             return this;
5077         },
5078
5079         /**
5080          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5081          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5082          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5083          * @param {String} title The title bar text
5084          * @param {String} msg The message box body text
5085          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5086          * @param {Object} scope (optional) The scope of the callback function
5087          * @return {Roo.MessageBox} This message box
5088          */
5089         confirm : function(title, msg, fn, scope){
5090             this.show({
5091                 title : title,
5092                 msg : msg,
5093                 buttons: this.YESNO,
5094                 fn: fn,
5095                 scope : scope,
5096                 modal : true
5097             });
5098             return this;
5099         },
5100
5101         /**
5102          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5103          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5104          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5105          * (could also be the top-right close button) and the text that was entered will be passed as the two
5106          * parameters to the callback.
5107          * @param {String} title The title bar text
5108          * @param {String} msg The message box body text
5109          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5110          * @param {Object} scope (optional) The scope of the callback function
5111          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5112          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5113          * @return {Roo.MessageBox} This message box
5114          */
5115         prompt : function(title, msg, fn, scope, multiline){
5116             this.show({
5117                 title : title,
5118                 msg : msg,
5119                 buttons: this.OKCANCEL,
5120                 fn: fn,
5121                 minWidth:250,
5122                 scope : scope,
5123                 prompt:true,
5124                 multiline: multiline,
5125                 modal : true
5126             });
5127             return this;
5128         },
5129
5130         /**
5131          * Button config that displays a single OK button
5132          * @type Object
5133          */
5134         OK : {ok:true},
5135         /**
5136          * Button config that displays Yes and No buttons
5137          * @type Object
5138          */
5139         YESNO : {yes:true, no:true},
5140         /**
5141          * Button config that displays OK and Cancel buttons
5142          * @type Object
5143          */
5144         OKCANCEL : {ok:true, cancel:true},
5145         /**
5146          * Button config that displays Yes, No and Cancel buttons
5147          * @type Object
5148          */
5149         YESNOCANCEL : {yes:true, no:true, cancel:true},
5150
5151         /**
5152          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5153          * @type Number
5154          */
5155         defaultTextHeight : 75,
5156         /**
5157          * The maximum width in pixels of the message box (defaults to 600)
5158          * @type Number
5159          */
5160         maxWidth : 600,
5161         /**
5162          * The minimum width in pixels of the message box (defaults to 100)
5163          * @type Number
5164          */
5165         minWidth : 100,
5166         /**
5167          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5168          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5169          * @type Number
5170          */
5171         minProgressWidth : 250,
5172         /**
5173          * An object containing the default button text strings that can be overriden for localized language support.
5174          * Supported properties are: ok, cancel, yes and no.
5175          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5176          * @type Object
5177          */
5178         buttonText : {
5179             ok : "OK",
5180             cancel : "Cancel",
5181             yes : "Yes",
5182             no : "No"
5183         }
5184     };
5185 }();
5186
5187 /**
5188  * Shorthand for {@link Roo.MessageBox}
5189  */
5190 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5191 Roo.Msg = Roo.Msg || Roo.MessageBox;
5192 /*
5193  * - LGPL
5194  *
5195  * navbar
5196  * 
5197  */
5198
5199 /**
5200  * @class Roo.bootstrap.Navbar
5201  * @extends Roo.bootstrap.Component
5202  * Bootstrap Navbar class
5203
5204  * @constructor
5205  * Create a new Navbar
5206  * @param {Object} config The config object
5207  */
5208
5209
5210 Roo.bootstrap.Navbar = function(config){
5211     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5212     this.addEvents({
5213         // raw events
5214         /**
5215          * @event beforetoggle
5216          * Fire before toggle the menu
5217          * @param {Roo.EventObject} e
5218          */
5219         "beforetoggle" : true
5220     });
5221 };
5222
5223 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5224     
5225     
5226    
5227     // private
5228     navItems : false,
5229     loadMask : false,
5230     
5231     
5232     getAutoCreate : function(){
5233         
5234         
5235         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5236         
5237     },
5238     
5239     initEvents :function ()
5240     {
5241         //Roo.log(this.el.select('.navbar-toggle',true));
5242         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5243         
5244         var mark = {
5245             tag: "div",
5246             cls:"x-dlg-mask"
5247         };
5248         
5249         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5250         
5251         var size = this.el.getSize();
5252         this.maskEl.setSize(size.width, size.height);
5253         this.maskEl.enableDisplayMode("block");
5254         this.maskEl.hide();
5255         
5256         if(this.loadMask){
5257             this.maskEl.show();
5258         }
5259     },
5260     
5261     
5262     getChildContainer : function()
5263     {
5264         if (this.el && this.el.select('.collapse').getCount()) {
5265             return this.el.select('.collapse',true).first();
5266         }
5267         
5268         return this.el;
5269     },
5270     
5271     mask : function()
5272     {
5273         this.maskEl.show();
5274     },
5275     
5276     unmask : function()
5277     {
5278         this.maskEl.hide();
5279     },
5280     onToggle : function()
5281     {
5282         
5283         if(this.fireEvent('beforetoggle', this) === false){
5284             return;
5285         }
5286         var ce = this.el.select('.navbar-collapse',true).first();
5287       
5288         if (!ce.hasClass('show')) {
5289            this.expand();
5290         } else {
5291             this.collapse();
5292         }
5293         
5294         
5295     
5296     },
5297     /**
5298      * Expand the navbar pulldown 
5299      */
5300     expand : function ()
5301     {
5302        
5303         var ce = this.el.select('.navbar-collapse',true).first();
5304         if (ce.hasClass('collapsing')) {
5305             return;
5306         }
5307         ce.dom.style.height = '';
5308                // show it...
5309         ce.addClass('in'); // old...
5310         ce.removeClass('collapse');
5311         ce.addClass('show');
5312         var h = ce.getHeight();
5313         Roo.log(h);
5314         ce.removeClass('show');
5315         // at this point we should be able to see it..
5316         ce.addClass('collapsing');
5317         
5318         ce.setHeight(0); // resize it ...
5319         ce.on('transitionend', function() {
5320             //Roo.log('done transition');
5321             ce.removeClass('collapsing');
5322             ce.addClass('show');
5323             ce.removeClass('collapse');
5324
5325             ce.dom.style.height = '';
5326         }, this, { single: true} );
5327         ce.setHeight(h);
5328         ce.dom.scrollTop = 0;
5329     },
5330     /**
5331      * Collapse the navbar pulldown 
5332      */
5333     collapse : function()
5334     {
5335          var ce = this.el.select('.navbar-collapse',true).first();
5336        
5337         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5338             // it's collapsed or collapsing..
5339             return;
5340         }
5341         ce.removeClass('in'); // old...
5342         ce.setHeight(ce.getHeight());
5343         ce.removeClass('show');
5344         ce.addClass('collapsing');
5345         
5346         ce.on('transitionend', function() {
5347             ce.dom.style.height = '';
5348             ce.removeClass('collapsing');
5349             ce.addClass('collapse');
5350         }, this, { single: true} );
5351         ce.setHeight(0);
5352     }
5353     
5354     
5355     
5356 });
5357
5358
5359
5360  
5361
5362  /*
5363  * - LGPL
5364  *
5365  * navbar
5366  * 
5367  */
5368
5369 /**
5370  * @class Roo.bootstrap.NavSimplebar
5371  * @extends Roo.bootstrap.Navbar
5372  * Bootstrap Sidebar class
5373  *
5374  * @cfg {Boolean} inverse is inverted color
5375  * 
5376  * @cfg {String} type (nav | pills | tabs)
5377  * @cfg {Boolean} arrangement stacked | justified
5378  * @cfg {String} align (left | right) alignment
5379  * 
5380  * @cfg {Boolean} main (true|false) main nav bar? default false
5381  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5382  * 
5383  * @cfg {String} tag (header|footer|nav|div) default is nav 
5384
5385  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5386  * 
5387  * 
5388  * @constructor
5389  * Create a new Sidebar
5390  * @param {Object} config The config object
5391  */
5392
5393
5394 Roo.bootstrap.NavSimplebar = function(config){
5395     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5396 };
5397
5398 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5399     
5400     inverse: false,
5401     
5402     type: false,
5403     arrangement: '',
5404     align : false,
5405     
5406     weight : 'light',
5407     
5408     main : false,
5409     
5410     
5411     tag : false,
5412     
5413     
5414     getAutoCreate : function(){
5415         
5416         
5417         var cfg = {
5418             tag : this.tag || 'div',
5419             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5420         };
5421         if (['light','white'].indexOf(this.weight) > -1) {
5422             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5423         }
5424         cfg.cls += ' bg-' + this.weight;
5425         
5426         if (this.inverse) {
5427             cfg.cls += ' navbar-inverse';
5428             
5429         }
5430         
5431         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5432         
5433         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5434             return cfg;
5435         }
5436         
5437         
5438     
5439         
5440         cfg.cn = [
5441             {
5442                 cls: 'nav nav-' + this.xtype,
5443                 tag : 'ul'
5444             }
5445         ];
5446         
5447          
5448         this.type = this.type || 'nav';
5449         if (['tabs','pills'].indexOf(this.type) != -1) {
5450             cfg.cn[0].cls += ' nav-' + this.type
5451         
5452         
5453         } else {
5454             if (this.type!=='nav') {
5455                 Roo.log('nav type must be nav/tabs/pills')
5456             }
5457             cfg.cn[0].cls += ' navbar-nav'
5458         }
5459         
5460         
5461         
5462         
5463         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5464             cfg.cn[0].cls += ' nav-' + this.arrangement;
5465         }
5466         
5467         
5468         if (this.align === 'right') {
5469             cfg.cn[0].cls += ' navbar-right';
5470         }
5471         
5472         
5473         
5474         
5475         return cfg;
5476     
5477         
5478     }
5479     
5480     
5481     
5482 });
5483
5484
5485
5486  
5487
5488  
5489        /*
5490  * - LGPL
5491  *
5492  * navbar
5493  * navbar-fixed-top
5494  * navbar-expand-md  fixed-top 
5495  */
5496
5497 /**
5498  * @class Roo.bootstrap.NavHeaderbar
5499  * @extends Roo.bootstrap.NavSimplebar
5500  * Bootstrap Sidebar class
5501  *
5502  * @cfg {String} brand what is brand
5503  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5504  * @cfg {String} brand_href href of the brand
5505  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5506  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5507  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5508  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5509  * 
5510  * @constructor
5511  * Create a new Sidebar
5512  * @param {Object} config The config object
5513  */
5514
5515
5516 Roo.bootstrap.NavHeaderbar = function(config){
5517     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5518       
5519 };
5520
5521 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5522     
5523     position: '',
5524     brand: '',
5525     brand_href: false,
5526     srButton : true,
5527     autohide : false,
5528     desktopCenter : false,
5529    
5530     
5531     getAutoCreate : function(){
5532         
5533         var   cfg = {
5534             tag: this.nav || 'nav',
5535             cls: 'navbar navbar-expand-md',
5536             role: 'navigation',
5537             cn: []
5538         };
5539         
5540         var cn = cfg.cn;
5541         if (this.desktopCenter) {
5542             cn.push({cls : 'container', cn : []});
5543             cn = cn[0].cn;
5544         }
5545         
5546         if(this.srButton){
5547             var btn = {
5548                 tag: 'button',
5549                 type: 'button',
5550                 cls: 'navbar-toggle navbar-toggler',
5551                 'data-toggle': 'collapse',
5552                 cn: [
5553                     {
5554                         tag: 'span',
5555                         cls: 'sr-only',
5556                         html: 'Toggle navigation'
5557                     },
5558                     {
5559                         tag: 'span',
5560                         cls: 'icon-bar navbar-toggler-icon'
5561                     },
5562                     {
5563                         tag: 'span',
5564                         cls: 'icon-bar'
5565                     },
5566                     {
5567                         tag: 'span',
5568                         cls: 'icon-bar'
5569                     }
5570                 ]
5571             };
5572             
5573             cn.push( Roo.bootstrap.version == 4 ? btn : {
5574                 tag: 'div',
5575                 cls: 'navbar-header',
5576                 cn: [
5577                     btn
5578                 ]
5579             });
5580         }
5581         
5582         cn.push({
5583             tag: 'div',
5584             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5585             cn : []
5586         });
5587         
5588         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5589         
5590         if (['light','white'].indexOf(this.weight) > -1) {
5591             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5592         }
5593         cfg.cls += ' bg-' + this.weight;
5594         
5595         
5596         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5597             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5598             
5599             // tag can override this..
5600             
5601             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5602         }
5603         
5604         if (this.brand !== '') {
5605             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5606             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5607                 tag: 'a',
5608                 href: this.brand_href ? this.brand_href : '#',
5609                 cls: 'navbar-brand',
5610                 cn: [
5611                 this.brand
5612                 ]
5613             });
5614         }
5615         
5616         if(this.main){
5617             cfg.cls += ' main-nav';
5618         }
5619         
5620         
5621         return cfg;
5622
5623         
5624     },
5625     getHeaderChildContainer : function()
5626     {
5627         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5628             return this.el.select('.navbar-header',true).first();
5629         }
5630         
5631         return this.getChildContainer();
5632     },
5633     
5634     getChildContainer : function()
5635     {
5636          
5637         return this.el.select('.roo-navbar-collapse',true).first();
5638          
5639         
5640     },
5641     
5642     initEvents : function()
5643     {
5644         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5645         
5646         if (this.autohide) {
5647             
5648             var prevScroll = 0;
5649             var ft = this.el;
5650             
5651             Roo.get(document).on('scroll',function(e) {
5652                 var ns = Roo.get(document).getScroll().top;
5653                 var os = prevScroll;
5654                 prevScroll = ns;
5655                 
5656                 if(ns > os){
5657                     ft.removeClass('slideDown');
5658                     ft.addClass('slideUp');
5659                     return;
5660                 }
5661                 ft.removeClass('slideUp');
5662                 ft.addClass('slideDown');
5663                  
5664               
5665           },this);
5666         }
5667     }    
5668     
5669 });
5670
5671
5672
5673  
5674
5675  /*
5676  * - LGPL
5677  *
5678  * navbar
5679  * 
5680  */
5681
5682 /**
5683  * @class Roo.bootstrap.NavSidebar
5684  * @extends Roo.bootstrap.Navbar
5685  * Bootstrap Sidebar class
5686  * 
5687  * @constructor
5688  * Create a new Sidebar
5689  * @param {Object} config The config object
5690  */
5691
5692
5693 Roo.bootstrap.NavSidebar = function(config){
5694     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5695 };
5696
5697 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5698     
5699     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5700     
5701     getAutoCreate : function(){
5702         
5703         
5704         return  {
5705             tag: 'div',
5706             cls: 'sidebar sidebar-nav'
5707         };
5708     
5709         
5710     }
5711     
5712     
5713     
5714 });
5715
5716
5717
5718  
5719
5720  /*
5721  * - LGPL
5722  *
5723  * nav group
5724  * 
5725  */
5726
5727 /**
5728  * @class Roo.bootstrap.NavGroup
5729  * @extends Roo.bootstrap.Component
5730  * Bootstrap NavGroup class
5731  * @cfg {String} align (left|right)
5732  * @cfg {Boolean} inverse
5733  * @cfg {String} type (nav|pills|tab) default nav
5734  * @cfg {String} navId - reference Id for navbar.
5735
5736  * 
5737  * @constructor
5738  * Create a new nav group
5739  * @param {Object} config The config object
5740  */
5741
5742 Roo.bootstrap.NavGroup = function(config){
5743     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5744     this.navItems = [];
5745    
5746     Roo.bootstrap.NavGroup.register(this);
5747      this.addEvents({
5748         /**
5749              * @event changed
5750              * Fires when the active item changes
5751              * @param {Roo.bootstrap.NavGroup} this
5752              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5753              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5754          */
5755         'changed': true
5756      });
5757     
5758 };
5759
5760 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5761     
5762     align: '',
5763     inverse: false,
5764     form: false,
5765     type: 'nav',
5766     navId : '',
5767     // private
5768     
5769     navItems : false, 
5770     
5771     getAutoCreate : function()
5772     {
5773         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5774         
5775         cfg = {
5776             tag : 'ul',
5777             cls: 'nav' 
5778         };
5779         if (Roo.bootstrap.version == 4) {
5780             if (['tabs','pills'].indexOf(this.type) != -1) {
5781                 cfg.cls += ' nav-' + this.type; 
5782             } else {
5783                 // trying to remove so header bar can right align top?
5784                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5785                     // do not use on header bar... 
5786                     cfg.cls += ' navbar-nav';
5787                 }
5788             }
5789             
5790         } else {
5791             if (['tabs','pills'].indexOf(this.type) != -1) {
5792                 cfg.cls += ' nav-' + this.type
5793             } else {
5794                 if (this.type !== 'nav') {
5795                     Roo.log('nav type must be nav/tabs/pills')
5796                 }
5797                 cfg.cls += ' navbar-nav'
5798             }
5799         }
5800         
5801         if (this.parent() && this.parent().sidebar) {
5802             cfg = {
5803                 tag: 'ul',
5804                 cls: 'dashboard-menu sidebar-menu'
5805             };
5806             
5807             return cfg;
5808         }
5809         
5810         if (this.form === true) {
5811             cfg = {
5812                 tag: 'form',
5813                 cls: 'navbar-form form-inline'
5814             };
5815             //nav navbar-right ml-md-auto
5816             if (this.align === 'right') {
5817                 cfg.cls += ' navbar-right ml-md-auto';
5818             } else {
5819                 cfg.cls += ' navbar-left';
5820             }
5821         }
5822         
5823         if (this.align === 'right') {
5824             cfg.cls += ' navbar-right ml-md-auto';
5825         } else {
5826             cfg.cls += ' mr-auto';
5827         }
5828         
5829         if (this.inverse) {
5830             cfg.cls += ' navbar-inverse';
5831             
5832         }
5833         
5834         
5835         return cfg;
5836     },
5837     /**
5838     * sets the active Navigation item
5839     * @param {Roo.bootstrap.NavItem} the new current navitem
5840     */
5841     setActiveItem : function(item)
5842     {
5843         var prev = false;
5844         Roo.each(this.navItems, function(v){
5845             if (v == item) {
5846                 return ;
5847             }
5848             if (v.isActive()) {
5849                 v.setActive(false, true);
5850                 prev = v;
5851                 
5852             }
5853             
5854         });
5855
5856         item.setActive(true, true);
5857         this.fireEvent('changed', this, item, prev);
5858         
5859         
5860     },
5861     /**
5862     * gets the active Navigation item
5863     * @return {Roo.bootstrap.NavItem} the current navitem
5864     */
5865     getActive : function()
5866     {
5867         
5868         var prev = false;
5869         Roo.each(this.navItems, function(v){
5870             
5871             if (v.isActive()) {
5872                 prev = v;
5873                 
5874             }
5875             
5876         });
5877         return prev;
5878     },
5879     
5880     indexOfNav : function()
5881     {
5882         
5883         var prev = false;
5884         Roo.each(this.navItems, function(v,i){
5885             
5886             if (v.isActive()) {
5887                 prev = i;
5888                 
5889             }
5890             
5891         });
5892         return prev;
5893     },
5894     /**
5895     * adds a Navigation item
5896     * @param {Roo.bootstrap.NavItem} the navitem to add
5897     */
5898     addItem : function(cfg)
5899     {
5900         if (this.form && Roo.bootstrap.version == 4) {
5901             cfg.tag = 'div';
5902         }
5903         var cn = new Roo.bootstrap.NavItem(cfg);
5904         this.register(cn);
5905         cn.parentId = this.id;
5906         cn.onRender(this.el, null);
5907         return cn;
5908     },
5909     /**
5910     * register a Navigation item
5911     * @param {Roo.bootstrap.NavItem} the navitem to add
5912     */
5913     register : function(item)
5914     {
5915         this.navItems.push( item);
5916         item.navId = this.navId;
5917     
5918     },
5919     
5920     /**
5921     * clear all the Navigation item
5922     */
5923    
5924     clearAll : function()
5925     {
5926         this.navItems = [];
5927         this.el.dom.innerHTML = '';
5928     },
5929     
5930     getNavItem: function(tabId)
5931     {
5932         var ret = false;
5933         Roo.each(this.navItems, function(e) {
5934             if (e.tabId == tabId) {
5935                ret =  e;
5936                return false;
5937             }
5938             return true;
5939             
5940         });
5941         return ret;
5942     },
5943     
5944     setActiveNext : function()
5945     {
5946         var i = this.indexOfNav(this.getActive());
5947         if (i > this.navItems.length) {
5948             return;
5949         }
5950         this.setActiveItem(this.navItems[i+1]);
5951     },
5952     setActivePrev : function()
5953     {
5954         var i = this.indexOfNav(this.getActive());
5955         if (i  < 1) {
5956             return;
5957         }
5958         this.setActiveItem(this.navItems[i-1]);
5959     },
5960     clearWasActive : function(except) {
5961         Roo.each(this.navItems, function(e) {
5962             if (e.tabId != except.tabId && e.was_active) {
5963                e.was_active = false;
5964                return false;
5965             }
5966             return true;
5967             
5968         });
5969     },
5970     getWasActive : function ()
5971     {
5972         var r = false;
5973         Roo.each(this.navItems, function(e) {
5974             if (e.was_active) {
5975                r = e;
5976                return false;
5977             }
5978             return true;
5979             
5980         });
5981         return r;
5982     }
5983     
5984     
5985 });
5986
5987  
5988 Roo.apply(Roo.bootstrap.NavGroup, {
5989     
5990     groups: {},
5991      /**
5992     * register a Navigation Group
5993     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5994     */
5995     register : function(navgrp)
5996     {
5997         this.groups[navgrp.navId] = navgrp;
5998         
5999     },
6000     /**
6001     * fetch a Navigation Group based on the navigation ID
6002     * @param {string} the navgroup to add
6003     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6004     */
6005     get: function(navId) {
6006         if (typeof(this.groups[navId]) == 'undefined') {
6007             return false;
6008             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6009         }
6010         return this.groups[navId] ;
6011     }
6012     
6013     
6014     
6015 });
6016
6017  /*
6018  * - LGPL
6019  *
6020  * row
6021  * 
6022  */
6023
6024 /**
6025  * @class Roo.bootstrap.NavItem
6026  * @extends Roo.bootstrap.Component
6027  * Bootstrap Navbar.NavItem class
6028  * @cfg {String} href  link to
6029  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6030
6031  * @cfg {String} html content of button
6032  * @cfg {String} badge text inside badge
6033  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6034  * @cfg {String} glyphicon DEPRICATED - use fa
6035  * @cfg {String} icon DEPRICATED - use fa
6036  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6037  * @cfg {Boolean} active Is item active
6038  * @cfg {Boolean} disabled Is item disabled
6039  
6040  * @cfg {Boolean} preventDefault (true | false) default false
6041  * @cfg {String} tabId the tab that this item activates.
6042  * @cfg {String} tagtype (a|span) render as a href or span?
6043  * @cfg {Boolean} animateRef (true|false) link to element default false  
6044   
6045  * @constructor
6046  * Create a new Navbar Item
6047  * @param {Object} config The config object
6048  */
6049 Roo.bootstrap.NavItem = function(config){
6050     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6051     this.addEvents({
6052         // raw events
6053         /**
6054          * @event click
6055          * The raw click event for the entire grid.
6056          * @param {Roo.EventObject} e
6057          */
6058         "click" : true,
6059          /**
6060             * @event changed
6061             * Fires when the active item active state changes
6062             * @param {Roo.bootstrap.NavItem} this
6063             * @param {boolean} state the new state
6064              
6065          */
6066         'changed': true,
6067         /**
6068             * @event scrollto
6069             * Fires when scroll to element
6070             * @param {Roo.bootstrap.NavItem} this
6071             * @param {Object} options
6072             * @param {Roo.EventObject} e
6073              
6074          */
6075         'scrollto': true
6076     });
6077    
6078 };
6079
6080 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6081     
6082     href: false,
6083     html: '',
6084     badge: '',
6085     icon: false,
6086     fa : false,
6087     glyphicon: false,
6088     active: false,
6089     preventDefault : false,
6090     tabId : false,
6091     tagtype : 'a',
6092     tag: 'li',
6093     disabled : false,
6094     animateRef : false,
6095     was_active : false,
6096     button_weight : '',
6097     button_outline : false,
6098     
6099     navLink: false,
6100     
6101     getAutoCreate : function(){
6102          
6103         var cfg = {
6104             tag: this.tag,
6105             cls: 'nav-item'
6106         };
6107         
6108         if (this.active) {
6109             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6110         }
6111         if (this.disabled) {
6112             cfg.cls += ' disabled';
6113         }
6114         
6115         // BS4 only?
6116         if (this.button_weight.length) {
6117             cfg.tag = this.href ? 'a' : 'button';
6118             cfg.html = this.html || '';
6119             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6120             if (this.href) {
6121                 cfg.href = this.href;
6122             }
6123             if (this.fa) {
6124                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6125             }
6126             
6127             // menu .. should add dropdown-menu class - so no need for carat..
6128             
6129             if (this.badge !== '') {
6130                  
6131                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6132             }
6133             return cfg;
6134         }
6135         
6136         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6137             cfg.cn = [
6138                 {
6139                     tag: this.tagtype,
6140                     href : this.href || "#",
6141                     html: this.html || ''
6142                 }
6143             ];
6144             if (this.tagtype == 'a') {
6145                 cfg.cn[0].cls = 'nav-link';
6146             }
6147             if (this.icon) {
6148                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6149             }
6150             if (this.fa) {
6151                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6152             }
6153             if(this.glyphicon) {
6154                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6155             }
6156             
6157             if (this.menu) {
6158                 
6159                 cfg.cn[0].html += " <span class='caret'></span>";
6160              
6161             }
6162             
6163             if (this.badge !== '') {
6164                  
6165                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6166             }
6167         }
6168         
6169         
6170         
6171         return cfg;
6172     },
6173     onRender : function(ct, position)
6174     {
6175        // Roo.log("Call onRender: " + this.xtype);
6176         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6177             this.tag = 'div';
6178         }
6179         
6180         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6181         this.navLink = this.el.select('.nav-link',true).first();
6182         return ret;
6183     },
6184       
6185     
6186     initEvents: function() 
6187     {
6188         if (typeof (this.menu) != 'undefined') {
6189             this.menu.parentType = this.xtype;
6190             this.menu.triggerEl = this.el;
6191             this.menu = this.addxtype(Roo.apply({}, this.menu));
6192         }
6193         
6194         this.el.select('a',true).on('click', this.onClick, this);
6195         
6196         if(this.tagtype == 'span'){
6197             this.el.select('span',true).on('click', this.onClick, this);
6198         }
6199        
6200         // at this point parent should be available..
6201         this.parent().register(this);
6202     },
6203     
6204     onClick : function(e)
6205     {
6206         if (e.getTarget('.dropdown-menu-item')) {
6207             // did you click on a menu itemm.... - then don't trigger onclick..
6208             return;
6209         }
6210         
6211         if(
6212                 this.preventDefault || 
6213                 this.href == '#' 
6214         ){
6215             Roo.log("NavItem - prevent Default?");
6216             e.preventDefault();
6217         }
6218         
6219         if (this.disabled) {
6220             return;
6221         }
6222         
6223         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6224         if (tg && tg.transition) {
6225             Roo.log("waiting for the transitionend");
6226             return;
6227         }
6228         
6229         
6230         
6231         //Roo.log("fire event clicked");
6232         if(this.fireEvent('click', this, e) === false){
6233             return;
6234         };
6235         
6236         if(this.tagtype == 'span'){
6237             return;
6238         }
6239         
6240         //Roo.log(this.href);
6241         var ael = this.el.select('a',true).first();
6242         //Roo.log(ael);
6243         
6244         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6245             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6246             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6247                 return; // ignore... - it's a 'hash' to another page.
6248             }
6249             Roo.log("NavItem - prevent Default?");
6250             e.preventDefault();
6251             this.scrollToElement(e);
6252         }
6253         
6254         
6255         var p =  this.parent();
6256    
6257         if (['tabs','pills'].indexOf(p.type)!==-1) {
6258             if (typeof(p.setActiveItem) !== 'undefined') {
6259                 p.setActiveItem(this);
6260             }
6261         }
6262         
6263         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6264         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6265             // remove the collapsed menu expand...
6266             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6267         }
6268     },
6269     
6270     isActive: function () {
6271         return this.active
6272     },
6273     setActive : function(state, fire, is_was_active)
6274     {
6275         if (this.active && !state && this.navId) {
6276             this.was_active = true;
6277             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6278             if (nv) {
6279                 nv.clearWasActive(this);
6280             }
6281             
6282         }
6283         this.active = state;
6284         
6285         if (!state ) {
6286             this.el.removeClass('active');
6287             this.navLink ? this.navLink.removeClass('active') : false;
6288         } else if (!this.el.hasClass('active')) {
6289             
6290             this.el.addClass('active');
6291             if (Roo.bootstrap.version == 4 && this.navLink ) {
6292                 this.navLink.addClass('active');
6293             }
6294             
6295         }
6296         if (fire) {
6297             this.fireEvent('changed', this, state);
6298         }
6299         
6300         // show a panel if it's registered and related..
6301         
6302         if (!this.navId || !this.tabId || !state || is_was_active) {
6303             return;
6304         }
6305         
6306         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6307         if (!tg) {
6308             return;
6309         }
6310         var pan = tg.getPanelByName(this.tabId);
6311         if (!pan) {
6312             return;
6313         }
6314         // if we can not flip to new panel - go back to old nav highlight..
6315         if (false == tg.showPanel(pan)) {
6316             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6317             if (nv) {
6318                 var onav = nv.getWasActive();
6319                 if (onav) {
6320                     onav.setActive(true, false, true);
6321                 }
6322             }
6323             
6324         }
6325         
6326         
6327         
6328     },
6329      // this should not be here...
6330     setDisabled : function(state)
6331     {
6332         this.disabled = state;
6333         if (!state ) {
6334             this.el.removeClass('disabled');
6335         } else if (!this.el.hasClass('disabled')) {
6336             this.el.addClass('disabled');
6337         }
6338         
6339     },
6340     
6341     /**
6342      * Fetch the element to display the tooltip on.
6343      * @return {Roo.Element} defaults to this.el
6344      */
6345     tooltipEl : function()
6346     {
6347         return this.el.select('' + this.tagtype + '', true).first();
6348     },
6349     
6350     scrollToElement : function(e)
6351     {
6352         var c = document.body;
6353         
6354         /*
6355          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6356          */
6357         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6358             c = document.documentElement;
6359         }
6360         
6361         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6362         
6363         if(!target){
6364             return;
6365         }
6366
6367         var o = target.calcOffsetsTo(c);
6368         
6369         var options = {
6370             target : target,
6371             value : o[1]
6372         };
6373         
6374         this.fireEvent('scrollto', this, options, e);
6375         
6376         Roo.get(c).scrollTo('top', options.value, true);
6377         
6378         return;
6379     }
6380 });
6381  
6382
6383  /*
6384  * - LGPL
6385  *
6386  * sidebar item
6387  *
6388  *  li
6389  *    <span> icon </span>
6390  *    <span> text </span>
6391  *    <span>badge </span>
6392  */
6393
6394 /**
6395  * @class Roo.bootstrap.NavSidebarItem
6396  * @extends Roo.bootstrap.NavItem
6397  * Bootstrap Navbar.NavSidebarItem class
6398  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6399  * {Boolean} open is the menu open
6400  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6401  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6402  * {String} buttonSize (sm|md|lg)the extra classes for the button
6403  * {Boolean} showArrow show arrow next to the text (default true)
6404  * @constructor
6405  * Create a new Navbar Button
6406  * @param {Object} config The config object
6407  */
6408 Roo.bootstrap.NavSidebarItem = function(config){
6409     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6410     this.addEvents({
6411         // raw events
6412         /**
6413          * @event click
6414          * The raw click event for the entire grid.
6415          * @param {Roo.EventObject} e
6416          */
6417         "click" : true,
6418          /**
6419             * @event changed
6420             * Fires when the active item active state changes
6421             * @param {Roo.bootstrap.NavSidebarItem} this
6422             * @param {boolean} state the new state
6423              
6424          */
6425         'changed': true
6426     });
6427    
6428 };
6429
6430 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6431     
6432     badgeWeight : 'default',
6433     
6434     open: false,
6435     
6436     buttonView : false,
6437     
6438     buttonWeight : 'default',
6439     
6440     buttonSize : 'md',
6441     
6442     showArrow : true,
6443     
6444     getAutoCreate : function(){
6445         
6446         
6447         var a = {
6448                 tag: 'a',
6449                 href : this.href || '#',
6450                 cls: '',
6451                 html : '',
6452                 cn : []
6453         };
6454         
6455         if(this.buttonView){
6456             a = {
6457                 tag: 'button',
6458                 href : this.href || '#',
6459                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6460                 html : this.html,
6461                 cn : []
6462             };
6463         }
6464         
6465         var cfg = {
6466             tag: 'li',
6467             cls: '',
6468             cn: [ a ]
6469         };
6470         
6471         if (this.active) {
6472             cfg.cls += ' active';
6473         }
6474         
6475         if (this.disabled) {
6476             cfg.cls += ' disabled';
6477         }
6478         if (this.open) {
6479             cfg.cls += ' open x-open';
6480         }
6481         // left icon..
6482         if (this.glyphicon || this.icon) {
6483             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6484             a.cn.push({ tag : 'i', cls : c }) ;
6485         }
6486         
6487         if(!this.buttonView){
6488             var span = {
6489                 tag: 'span',
6490                 html : this.html || ''
6491             };
6492
6493             a.cn.push(span);
6494             
6495         }
6496         
6497         if (this.badge !== '') {
6498             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6499         }
6500         
6501         if (this.menu) {
6502             
6503             if(this.showArrow){
6504                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6505             }
6506             
6507             a.cls += ' dropdown-toggle treeview' ;
6508         }
6509         
6510         return cfg;
6511     },
6512     
6513     initEvents : function()
6514     { 
6515         if (typeof (this.menu) != 'undefined') {
6516             this.menu.parentType = this.xtype;
6517             this.menu.triggerEl = this.el;
6518             this.menu = this.addxtype(Roo.apply({}, this.menu));
6519         }
6520         
6521         this.el.on('click', this.onClick, this);
6522         
6523         if(this.badge !== ''){
6524             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6525         }
6526         
6527     },
6528     
6529     onClick : function(e)
6530     {
6531         if(this.disabled){
6532             e.preventDefault();
6533             return;
6534         }
6535         
6536         if(this.preventDefault){
6537             e.preventDefault();
6538         }
6539         
6540         this.fireEvent('click', this, e);
6541     },
6542     
6543     disable : function()
6544     {
6545         this.setDisabled(true);
6546     },
6547     
6548     enable : function()
6549     {
6550         this.setDisabled(false);
6551     },
6552     
6553     setDisabled : function(state)
6554     {
6555         if(this.disabled == state){
6556             return;
6557         }
6558         
6559         this.disabled = state;
6560         
6561         if (state) {
6562             this.el.addClass('disabled');
6563             return;
6564         }
6565         
6566         this.el.removeClass('disabled');
6567         
6568         return;
6569     },
6570     
6571     setActive : function(state)
6572     {
6573         if(this.active == state){
6574             return;
6575         }
6576         
6577         this.active = state;
6578         
6579         if (state) {
6580             this.el.addClass('active');
6581             return;
6582         }
6583         
6584         this.el.removeClass('active');
6585         
6586         return;
6587     },
6588     
6589     isActive: function () 
6590     {
6591         return this.active;
6592     },
6593     
6594     setBadge : function(str)
6595     {
6596         if(!this.badgeEl){
6597             return;
6598         }
6599         
6600         this.badgeEl.dom.innerHTML = str;
6601     }
6602     
6603    
6604      
6605  
6606 });
6607  
6608
6609  /*
6610  * - LGPL
6611  *
6612  *  Breadcrumb Nav
6613  * 
6614  */
6615 Roo.namespace('Roo.bootstrap.breadcrumb');
6616
6617
6618 /**
6619  * @class Roo.bootstrap.breadcrumb.Nav
6620  * @extends Roo.bootstrap.Component
6621  * Bootstrap Breadcrumb Nav Class
6622  *  
6623  * @children Roo.bootstrap.breadcrumb.Item
6624  * 
6625  * @constructor
6626  * Create a new breadcrumb.Nav
6627  * @param {Object} config The config object
6628  */
6629
6630
6631 Roo.bootstrap.breadcrumb.Nav = function(config){
6632     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6633     
6634     
6635 };
6636
6637 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6638     
6639     getAutoCreate : function()
6640     {
6641
6642         var cfg = {
6643             tag: 'nav',
6644             cn : [
6645                 {
6646                     tag : 'ol',
6647                     cls : 'breadcrumb'
6648                 }
6649             ]
6650             
6651         };
6652           
6653         return cfg;
6654     },
6655     
6656     initEvents: function()
6657     {
6658         this.olEl = this.el.select('ol',true).first();    
6659     },
6660     getChildContainer : function()
6661     {
6662         return this.olEl;  
6663     }
6664     
6665 });
6666
6667  /*
6668  * - LGPL
6669  *
6670  *  Breadcrumb Item
6671  * 
6672  */
6673
6674
6675 /**
6676  * @class Roo.bootstrap.breadcrumb.Nav
6677  * @extends Roo.bootstrap.Component
6678  * Bootstrap Breadcrumb Nav Class
6679  *  
6680  * @children Roo.bootstrap.breadcrumb.Component
6681  * @cfg {String} html the content of the link.
6682  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6683  * @cfg {Boolean} active is it active
6684
6685  * 
6686  * @constructor
6687  * Create a new breadcrumb.Nav
6688  * @param {Object} config The config object
6689  */
6690
6691 Roo.bootstrap.breadcrumb.Item = function(config){
6692     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6693     this.addEvents({
6694         // img events
6695         /**
6696          * @event click
6697          * The img click event for the img.
6698          * @param {Roo.EventObject} e
6699          */
6700         "click" : true
6701     });
6702     
6703 };
6704
6705 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6706     
6707     href: false,
6708     html : '',
6709     
6710     getAutoCreate : function()
6711     {
6712
6713         var cfg = {
6714             tag: 'li',
6715             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6716         };
6717         if (this.href !== false) {
6718             cfg.cn = [{
6719                 tag : 'a',
6720                 href : this.href,
6721                 html : this.html
6722             }];
6723         } else {
6724             cfg.html = this.html;
6725         }
6726         
6727         return cfg;
6728     },
6729     
6730     initEvents: function()
6731     {
6732         if (this.href) {
6733             this.el.select('a', true).first().on('click',this.onClick, this)
6734         }
6735         
6736     },
6737     onClick : function(e)
6738     {
6739         e.preventDefault();
6740         this.fireEvent('click',this,  e);
6741     }
6742     
6743 });
6744
6745  /*
6746  * - LGPL
6747  *
6748  * row
6749  * 
6750  */
6751
6752 /**
6753  * @class Roo.bootstrap.Row
6754  * @extends Roo.bootstrap.Component
6755  * Bootstrap Row class (contains columns...)
6756  * 
6757  * @constructor
6758  * Create a new Row
6759  * @param {Object} config The config object
6760  */
6761
6762 Roo.bootstrap.Row = function(config){
6763     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6764 };
6765
6766 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6767     
6768     getAutoCreate : function(){
6769        return {
6770             cls: 'row clearfix'
6771        };
6772     }
6773     
6774     
6775 });
6776
6777  
6778
6779  /*
6780  * - LGPL
6781  *
6782  * pagination
6783  * 
6784  */
6785
6786 /**
6787  * @class Roo.bootstrap.Pagination
6788  * @extends Roo.bootstrap.Component
6789  * Bootstrap Pagination class
6790  * @cfg {String} size xs | sm | md | lg
6791  * @cfg {Boolean} inverse false | true
6792  * 
6793  * @constructor
6794  * Create a new Pagination
6795  * @param {Object} config The config object
6796  */
6797
6798 Roo.bootstrap.Pagination = function(config){
6799     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6800 };
6801
6802 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6803     
6804     cls: false,
6805     size: false,
6806     inverse: false,
6807     
6808     getAutoCreate : function(){
6809         var cfg = {
6810             tag: 'ul',
6811                 cls: 'pagination'
6812         };
6813         if (this.inverse) {
6814             cfg.cls += ' inverse';
6815         }
6816         if (this.html) {
6817             cfg.html=this.html;
6818         }
6819         if (this.cls) {
6820             cfg.cls += " " + this.cls;
6821         }
6822         return cfg;
6823     }
6824    
6825 });
6826
6827  
6828
6829  /*
6830  * - LGPL
6831  *
6832  * Pagination item
6833  * 
6834  */
6835
6836
6837 /**
6838  * @class Roo.bootstrap.PaginationItem
6839  * @extends Roo.bootstrap.Component
6840  * Bootstrap PaginationItem class
6841  * @cfg {String} html text
6842  * @cfg {String} href the link
6843  * @cfg {Boolean} preventDefault (true | false) default true
6844  * @cfg {Boolean} active (true | false) default false
6845  * @cfg {Boolean} disabled default false
6846  * 
6847  * 
6848  * @constructor
6849  * Create a new PaginationItem
6850  * @param {Object} config The config object
6851  */
6852
6853
6854 Roo.bootstrap.PaginationItem = function(config){
6855     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6856     this.addEvents({
6857         // raw events
6858         /**
6859          * @event click
6860          * The raw click event for the entire grid.
6861          * @param {Roo.EventObject} e
6862          */
6863         "click" : true
6864     });
6865 };
6866
6867 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6868     
6869     href : false,
6870     html : false,
6871     preventDefault: true,
6872     active : false,
6873     cls : false,
6874     disabled: false,
6875     
6876     getAutoCreate : function(){
6877         var cfg= {
6878             tag: 'li',
6879             cn: [
6880                 {
6881                     tag : 'a',
6882                     href : this.href ? this.href : '#',
6883                     html : this.html ? this.html : ''
6884                 }
6885             ]
6886         };
6887         
6888         if(this.cls){
6889             cfg.cls = this.cls;
6890         }
6891         
6892         if(this.disabled){
6893             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6894         }
6895         
6896         if(this.active){
6897             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6898         }
6899         
6900         return cfg;
6901     },
6902     
6903     initEvents: function() {
6904         
6905         this.el.on('click', this.onClick, this);
6906         
6907     },
6908     onClick : function(e)
6909     {
6910         Roo.log('PaginationItem on click ');
6911         if(this.preventDefault){
6912             e.preventDefault();
6913         }
6914         
6915         if(this.disabled){
6916             return;
6917         }
6918         
6919         this.fireEvent('click', this, e);
6920     }
6921    
6922 });
6923
6924  
6925
6926  /*
6927  * - LGPL
6928  *
6929  * slider
6930  * 
6931  */
6932
6933
6934 /**
6935  * @class Roo.bootstrap.Slider
6936  * @extends Roo.bootstrap.Component
6937  * Bootstrap Slider class
6938  *    
6939  * @constructor
6940  * Create a new Slider
6941  * @param {Object} config The config object
6942  */
6943
6944 Roo.bootstrap.Slider = function(config){
6945     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6946 };
6947
6948 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6949     
6950     getAutoCreate : function(){
6951         
6952         var cfg = {
6953             tag: 'div',
6954             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6955             cn: [
6956                 {
6957                     tag: 'a',
6958                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6959                 }
6960             ]
6961         };
6962         
6963         return cfg;
6964     }
6965    
6966 });
6967
6968  /*
6969  * Based on:
6970  * Ext JS Library 1.1.1
6971  * Copyright(c) 2006-2007, Ext JS, LLC.
6972  *
6973  * Originally Released Under LGPL - original licence link has changed is not relivant.
6974  *
6975  * Fork - LGPL
6976  * <script type="text/javascript">
6977  */
6978  
6979
6980 /**
6981  * @class Roo.grid.ColumnModel
6982  * @extends Roo.util.Observable
6983  * This is the default implementation of a ColumnModel used by the Grid. It defines
6984  * the columns in the grid.
6985  * <br>Usage:<br>
6986  <pre><code>
6987  var colModel = new Roo.grid.ColumnModel([
6988         {header: "Ticker", width: 60, sortable: true, locked: true},
6989         {header: "Company Name", width: 150, sortable: true},
6990         {header: "Market Cap.", width: 100, sortable: true},
6991         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6992         {header: "Employees", width: 100, sortable: true, resizable: false}
6993  ]);
6994  </code></pre>
6995  * <p>
6996  
6997  * The config options listed for this class are options which may appear in each
6998  * individual column definition.
6999  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7000  * @constructor
7001  * @param {Object} config An Array of column config objects. See this class's
7002  * config objects for details.
7003 */
7004 Roo.grid.ColumnModel = function(config){
7005         /**
7006      * The config passed into the constructor
7007      */
7008     this.config = config;
7009     this.lookup = {};
7010
7011     // if no id, create one
7012     // if the column does not have a dataIndex mapping,
7013     // map it to the order it is in the config
7014     for(var i = 0, len = config.length; i < len; i++){
7015         var c = config[i];
7016         if(typeof c.dataIndex == "undefined"){
7017             c.dataIndex = i;
7018         }
7019         if(typeof c.renderer == "string"){
7020             c.renderer = Roo.util.Format[c.renderer];
7021         }
7022         if(typeof c.id == "undefined"){
7023             c.id = Roo.id();
7024         }
7025         if(c.editor && c.editor.xtype){
7026             c.editor  = Roo.factory(c.editor, Roo.grid);
7027         }
7028         if(c.editor && c.editor.isFormField){
7029             c.editor = new Roo.grid.GridEditor(c.editor);
7030         }
7031         this.lookup[c.id] = c;
7032     }
7033
7034     /**
7035      * The width of columns which have no width specified (defaults to 100)
7036      * @type Number
7037      */
7038     this.defaultWidth = 100;
7039
7040     /**
7041      * Default sortable of columns which have no sortable specified (defaults to false)
7042      * @type Boolean
7043      */
7044     this.defaultSortable = false;
7045
7046     this.addEvents({
7047         /**
7048              * @event widthchange
7049              * Fires when the width of a column changes.
7050              * @param {ColumnModel} this
7051              * @param {Number} columnIndex The column index
7052              * @param {Number} newWidth The new width
7053              */
7054             "widthchange": true,
7055         /**
7056              * @event headerchange
7057              * Fires when the text of a header changes.
7058              * @param {ColumnModel} this
7059              * @param {Number} columnIndex The column index
7060              * @param {Number} newText The new header text
7061              */
7062             "headerchange": true,
7063         /**
7064              * @event hiddenchange
7065              * Fires when a column is hidden or "unhidden".
7066              * @param {ColumnModel} this
7067              * @param {Number} columnIndex The column index
7068              * @param {Boolean} hidden true if hidden, false otherwise
7069              */
7070             "hiddenchange": true,
7071             /**
7072          * @event columnmoved
7073          * Fires when a column is moved.
7074          * @param {ColumnModel} this
7075          * @param {Number} oldIndex
7076          * @param {Number} newIndex
7077          */
7078         "columnmoved" : true,
7079         /**
7080          * @event columlockchange
7081          * Fires when a column's locked state is changed
7082          * @param {ColumnModel} this
7083          * @param {Number} colIndex
7084          * @param {Boolean} locked true if locked
7085          */
7086         "columnlockchange" : true
7087     });
7088     Roo.grid.ColumnModel.superclass.constructor.call(this);
7089 };
7090 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7091     /**
7092      * @cfg {String} header The header text to display in the Grid view.
7093      */
7094     /**
7095      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7096      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7097      * specified, the column's index is used as an index into the Record's data Array.
7098      */
7099     /**
7100      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7101      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7102      */
7103     /**
7104      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7105      * Defaults to the value of the {@link #defaultSortable} property.
7106      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7107      */
7108     /**
7109      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7110      */
7111     /**
7112      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7113      */
7114     /**
7115      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7116      */
7117     /**
7118      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7119      */
7120     /**
7121      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7122      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7123      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7124      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7125      */
7126        /**
7127      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7128      */
7129     /**
7130      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7131      */
7132     /**
7133      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7134      */
7135     /**
7136      * @cfg {String} cursor (Optional)
7137      */
7138     /**
7139      * @cfg {String} tooltip (Optional)
7140      */
7141     /**
7142      * @cfg {Number} xs (Optional)
7143      */
7144     /**
7145      * @cfg {Number} sm (Optional)
7146      */
7147     /**
7148      * @cfg {Number} md (Optional)
7149      */
7150     /**
7151      * @cfg {Number} lg (Optional)
7152      */
7153     /**
7154      * Returns the id of the column at the specified index.
7155      * @param {Number} index The column index
7156      * @return {String} the id
7157      */
7158     getColumnId : function(index){
7159         return this.config[index].id;
7160     },
7161
7162     /**
7163      * Returns the column for a specified id.
7164      * @param {String} id The column id
7165      * @return {Object} the column
7166      */
7167     getColumnById : function(id){
7168         return this.lookup[id];
7169     },
7170
7171     
7172     /**
7173      * Returns the column for a specified dataIndex.
7174      * @param {String} dataIndex The column dataIndex
7175      * @return {Object|Boolean} the column or false if not found
7176      */
7177     getColumnByDataIndex: function(dataIndex){
7178         var index = this.findColumnIndex(dataIndex);
7179         return index > -1 ? this.config[index] : false;
7180     },
7181     
7182     /**
7183      * Returns the index for a specified column id.
7184      * @param {String} id The column id
7185      * @return {Number} the index, or -1 if not found
7186      */
7187     getIndexById : function(id){
7188         for(var i = 0, len = this.config.length; i < len; i++){
7189             if(this.config[i].id == id){
7190                 return i;
7191             }
7192         }
7193         return -1;
7194     },
7195     
7196     /**
7197      * Returns the index for a specified column dataIndex.
7198      * @param {String} dataIndex The column dataIndex
7199      * @return {Number} the index, or -1 if not found
7200      */
7201     
7202     findColumnIndex : function(dataIndex){
7203         for(var i = 0, len = this.config.length; i < len; i++){
7204             if(this.config[i].dataIndex == dataIndex){
7205                 return i;
7206             }
7207         }
7208         return -1;
7209     },
7210     
7211     
7212     moveColumn : function(oldIndex, newIndex){
7213         var c = this.config[oldIndex];
7214         this.config.splice(oldIndex, 1);
7215         this.config.splice(newIndex, 0, c);
7216         this.dataMap = null;
7217         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7218     },
7219
7220     isLocked : function(colIndex){
7221         return this.config[colIndex].locked === true;
7222     },
7223
7224     setLocked : function(colIndex, value, suppressEvent){
7225         if(this.isLocked(colIndex) == value){
7226             return;
7227         }
7228         this.config[colIndex].locked = value;
7229         if(!suppressEvent){
7230             this.fireEvent("columnlockchange", this, colIndex, value);
7231         }
7232     },
7233
7234     getTotalLockedWidth : function(){
7235         var totalWidth = 0;
7236         for(var i = 0; i < this.config.length; i++){
7237             if(this.isLocked(i) && !this.isHidden(i)){
7238                 this.totalWidth += this.getColumnWidth(i);
7239             }
7240         }
7241         return totalWidth;
7242     },
7243
7244     getLockedCount : function(){
7245         for(var i = 0, len = this.config.length; i < len; i++){
7246             if(!this.isLocked(i)){
7247                 return i;
7248             }
7249         }
7250         
7251         return this.config.length;
7252     },
7253
7254     /**
7255      * Returns the number of columns.
7256      * @return {Number}
7257      */
7258     getColumnCount : function(visibleOnly){
7259         if(visibleOnly === true){
7260             var c = 0;
7261             for(var i = 0, len = this.config.length; i < len; i++){
7262                 if(!this.isHidden(i)){
7263                     c++;
7264                 }
7265             }
7266             return c;
7267         }
7268         return this.config.length;
7269     },
7270
7271     /**
7272      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7273      * @param {Function} fn
7274      * @param {Object} scope (optional)
7275      * @return {Array} result
7276      */
7277     getColumnsBy : function(fn, scope){
7278         var r = [];
7279         for(var i = 0, len = this.config.length; i < len; i++){
7280             var c = this.config[i];
7281             if(fn.call(scope||this, c, i) === true){
7282                 r[r.length] = c;
7283             }
7284         }
7285         return r;
7286     },
7287
7288     /**
7289      * Returns true if the specified column is sortable.
7290      * @param {Number} col The column index
7291      * @return {Boolean}
7292      */
7293     isSortable : function(col){
7294         if(typeof this.config[col].sortable == "undefined"){
7295             return this.defaultSortable;
7296         }
7297         return this.config[col].sortable;
7298     },
7299
7300     /**
7301      * Returns the rendering (formatting) function defined for the column.
7302      * @param {Number} col The column index.
7303      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7304      */
7305     getRenderer : function(col){
7306         if(!this.config[col].renderer){
7307             return Roo.grid.ColumnModel.defaultRenderer;
7308         }
7309         return this.config[col].renderer;
7310     },
7311
7312     /**
7313      * Sets the rendering (formatting) function for a column.
7314      * @param {Number} col The column index
7315      * @param {Function} fn The function to use to process the cell's raw data
7316      * to return HTML markup for the grid view. The render function is called with
7317      * the following parameters:<ul>
7318      * <li>Data value.</li>
7319      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7320      * <li>css A CSS style string to apply to the table cell.</li>
7321      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7322      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7323      * <li>Row index</li>
7324      * <li>Column index</li>
7325      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7326      */
7327     setRenderer : function(col, fn){
7328         this.config[col].renderer = fn;
7329     },
7330
7331     /**
7332      * Returns the width for the specified column.
7333      * @param {Number} col The column index
7334      * @return {Number}
7335      */
7336     getColumnWidth : function(col){
7337         return this.config[col].width * 1 || this.defaultWidth;
7338     },
7339
7340     /**
7341      * Sets the width for a column.
7342      * @param {Number} col The column index
7343      * @param {Number} width The new width
7344      */
7345     setColumnWidth : function(col, width, suppressEvent){
7346         this.config[col].width = width;
7347         this.totalWidth = null;
7348         if(!suppressEvent){
7349              this.fireEvent("widthchange", this, col, width);
7350         }
7351     },
7352
7353     /**
7354      * Returns the total width of all columns.
7355      * @param {Boolean} includeHidden True to include hidden column widths
7356      * @return {Number}
7357      */
7358     getTotalWidth : function(includeHidden){
7359         if(!this.totalWidth){
7360             this.totalWidth = 0;
7361             for(var i = 0, len = this.config.length; i < len; i++){
7362                 if(includeHidden || !this.isHidden(i)){
7363                     this.totalWidth += this.getColumnWidth(i);
7364                 }
7365             }
7366         }
7367         return this.totalWidth;
7368     },
7369
7370     /**
7371      * Returns the header for the specified column.
7372      * @param {Number} col The column index
7373      * @return {String}
7374      */
7375     getColumnHeader : function(col){
7376         return this.config[col].header;
7377     },
7378
7379     /**
7380      * Sets the header for a column.
7381      * @param {Number} col The column index
7382      * @param {String} header The new header
7383      */
7384     setColumnHeader : function(col, header){
7385         this.config[col].header = header;
7386         this.fireEvent("headerchange", this, col, header);
7387     },
7388
7389     /**
7390      * Returns the tooltip for the specified column.
7391      * @param {Number} col The column index
7392      * @return {String}
7393      */
7394     getColumnTooltip : function(col){
7395             return this.config[col].tooltip;
7396     },
7397     /**
7398      * Sets the tooltip for a column.
7399      * @param {Number} col The column index
7400      * @param {String} tooltip The new tooltip
7401      */
7402     setColumnTooltip : function(col, tooltip){
7403             this.config[col].tooltip = tooltip;
7404     },
7405
7406     /**
7407      * Returns the dataIndex for the specified column.
7408      * @param {Number} col The column index
7409      * @return {Number}
7410      */
7411     getDataIndex : function(col){
7412         return this.config[col].dataIndex;
7413     },
7414
7415     /**
7416      * Sets the dataIndex for a column.
7417      * @param {Number} col The column index
7418      * @param {Number} dataIndex The new dataIndex
7419      */
7420     setDataIndex : function(col, dataIndex){
7421         this.config[col].dataIndex = dataIndex;
7422     },
7423
7424     
7425     
7426     /**
7427      * Returns true if the cell is editable.
7428      * @param {Number} colIndex The column index
7429      * @param {Number} rowIndex The row index - this is nto actually used..?
7430      * @return {Boolean}
7431      */
7432     isCellEditable : function(colIndex, rowIndex){
7433         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7434     },
7435
7436     /**
7437      * Returns the editor defined for the cell/column.
7438      * return false or null to disable editing.
7439      * @param {Number} colIndex The column index
7440      * @param {Number} rowIndex The row index
7441      * @return {Object}
7442      */
7443     getCellEditor : function(colIndex, rowIndex){
7444         return this.config[colIndex].editor;
7445     },
7446
7447     /**
7448      * Sets if a column is editable.
7449      * @param {Number} col The column index
7450      * @param {Boolean} editable True if the column is editable
7451      */
7452     setEditable : function(col, editable){
7453         this.config[col].editable = editable;
7454     },
7455
7456
7457     /**
7458      * Returns true if the column is hidden.
7459      * @param {Number} colIndex The column index
7460      * @return {Boolean}
7461      */
7462     isHidden : function(colIndex){
7463         return this.config[colIndex].hidden;
7464     },
7465
7466
7467     /**
7468      * Returns true if the column width cannot be changed
7469      */
7470     isFixed : function(colIndex){
7471         return this.config[colIndex].fixed;
7472     },
7473
7474     /**
7475      * Returns true if the column can be resized
7476      * @return {Boolean}
7477      */
7478     isResizable : function(colIndex){
7479         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7480     },
7481     /**
7482      * Sets if a column is hidden.
7483      * @param {Number} colIndex The column index
7484      * @param {Boolean} hidden True if the column is hidden
7485      */
7486     setHidden : function(colIndex, hidden){
7487         this.config[colIndex].hidden = hidden;
7488         this.totalWidth = null;
7489         this.fireEvent("hiddenchange", this, colIndex, hidden);
7490     },
7491
7492     /**
7493      * Sets the editor for a column.
7494      * @param {Number} col The column index
7495      * @param {Object} editor The editor object
7496      */
7497     setEditor : function(col, editor){
7498         this.config[col].editor = editor;
7499     }
7500 });
7501
7502 Roo.grid.ColumnModel.defaultRenderer = function(value)
7503 {
7504     if(typeof value == "object") {
7505         return value;
7506     }
7507         if(typeof value == "string" && value.length < 1){
7508             return "&#160;";
7509         }
7510     
7511         return String.format("{0}", value);
7512 };
7513
7514 // Alias for backwards compatibility
7515 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7516 /*
7517  * Based on:
7518  * Ext JS Library 1.1.1
7519  * Copyright(c) 2006-2007, Ext JS, LLC.
7520  *
7521  * Originally Released Under LGPL - original licence link has changed is not relivant.
7522  *
7523  * Fork - LGPL
7524  * <script type="text/javascript">
7525  */
7526  
7527 /**
7528  * @class Roo.LoadMask
7529  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7530  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7531  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7532  * element's UpdateManager load indicator and will be destroyed after the initial load.
7533  * @constructor
7534  * Create a new LoadMask
7535  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7536  * @param {Object} config The config object
7537  */
7538 Roo.LoadMask = function(el, config){
7539     this.el = Roo.get(el);
7540     Roo.apply(this, config);
7541     if(this.store){
7542         this.store.on('beforeload', this.onBeforeLoad, this);
7543         this.store.on('load', this.onLoad, this);
7544         this.store.on('loadexception', this.onLoadException, this);
7545         this.removeMask = false;
7546     }else{
7547         var um = this.el.getUpdateManager();
7548         um.showLoadIndicator = false; // disable the default indicator
7549         um.on('beforeupdate', this.onBeforeLoad, this);
7550         um.on('update', this.onLoad, this);
7551         um.on('failure', this.onLoad, this);
7552         this.removeMask = true;
7553     }
7554 };
7555
7556 Roo.LoadMask.prototype = {
7557     /**
7558      * @cfg {Boolean} removeMask
7559      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7560      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7561      */
7562     /**
7563      * @cfg {String} msg
7564      * The text to display in a centered loading message box (defaults to 'Loading...')
7565      */
7566     msg : 'Loading...',
7567     /**
7568      * @cfg {String} msgCls
7569      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7570      */
7571     msgCls : 'x-mask-loading',
7572
7573     /**
7574      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7575      * @type Boolean
7576      */
7577     disabled: false,
7578
7579     /**
7580      * Disables the mask to prevent it from being displayed
7581      */
7582     disable : function(){
7583        this.disabled = true;
7584     },
7585
7586     /**
7587      * Enables the mask so that it can be displayed
7588      */
7589     enable : function(){
7590         this.disabled = false;
7591     },
7592     
7593     onLoadException : function()
7594     {
7595         Roo.log(arguments);
7596         
7597         if (typeof(arguments[3]) != 'undefined') {
7598             Roo.MessageBox.alert("Error loading",arguments[3]);
7599         } 
7600         /*
7601         try {
7602             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7603                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7604             }   
7605         } catch(e) {
7606             
7607         }
7608         */
7609     
7610         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7611     },
7612     // private
7613     onLoad : function()
7614     {
7615         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7616     },
7617
7618     // private
7619     onBeforeLoad : function(){
7620         if(!this.disabled){
7621             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7622         }
7623     },
7624
7625     // private
7626     destroy : function(){
7627         if(this.store){
7628             this.store.un('beforeload', this.onBeforeLoad, this);
7629             this.store.un('load', this.onLoad, this);
7630             this.store.un('loadexception', this.onLoadException, this);
7631         }else{
7632             var um = this.el.getUpdateManager();
7633             um.un('beforeupdate', this.onBeforeLoad, this);
7634             um.un('update', this.onLoad, this);
7635             um.un('failure', this.onLoad, this);
7636         }
7637     }
7638 };/*
7639  * - LGPL
7640  *
7641  * table
7642  * 
7643  */
7644
7645 /**
7646  * @class Roo.bootstrap.Table
7647  * @extends Roo.bootstrap.Component
7648  * Bootstrap Table class
7649  * @cfg {String} cls table class
7650  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7651  * @cfg {String} bgcolor Specifies the background color for a table
7652  * @cfg {Number} border Specifies whether the table cells should have borders or not
7653  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7654  * @cfg {Number} cellspacing Specifies the space between cells
7655  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7656  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7657  * @cfg {String} sortable Specifies that the table should be sortable
7658  * @cfg {String} summary Specifies a summary of the content of a table
7659  * @cfg {Number} width Specifies the width of a table
7660  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7661  * 
7662  * @cfg {boolean} striped Should the rows be alternative striped
7663  * @cfg {boolean} bordered Add borders to the table
7664  * @cfg {boolean} hover Add hover highlighting
7665  * @cfg {boolean} condensed Format condensed
7666  * @cfg {boolean} responsive Format condensed
7667  * @cfg {Boolean} loadMask (true|false) default false
7668  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7669  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7670  * @cfg {Boolean} rowSelection (true|false) default false
7671  * @cfg {Boolean} cellSelection (true|false) default false
7672  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7673  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7674  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7675  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7676  
7677  * 
7678  * @constructor
7679  * Create a new Table
7680  * @param {Object} config The config object
7681  */
7682
7683 Roo.bootstrap.Table = function(config){
7684     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7685     
7686   
7687     
7688     // BC...
7689     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7690     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7691     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7692     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7693     
7694     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7695     if (this.sm) {
7696         this.sm.grid = this;
7697         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7698         this.sm = this.selModel;
7699         this.sm.xmodule = this.xmodule || false;
7700     }
7701     
7702     if (this.cm && typeof(this.cm.config) == 'undefined') {
7703         this.colModel = new Roo.grid.ColumnModel(this.cm);
7704         this.cm = this.colModel;
7705         this.cm.xmodule = this.xmodule || false;
7706     }
7707     if (this.store) {
7708         this.store= Roo.factory(this.store, Roo.data);
7709         this.ds = this.store;
7710         this.ds.xmodule = this.xmodule || false;
7711          
7712     }
7713     if (this.footer && this.store) {
7714         this.footer.dataSource = this.ds;
7715         this.footer = Roo.factory(this.footer);
7716     }
7717     
7718     /** @private */
7719     this.addEvents({
7720         /**
7721          * @event cellclick
7722          * Fires when a cell is clicked
7723          * @param {Roo.bootstrap.Table} this
7724          * @param {Roo.Element} el
7725          * @param {Number} rowIndex
7726          * @param {Number} columnIndex
7727          * @param {Roo.EventObject} e
7728          */
7729         "cellclick" : true,
7730         /**
7731          * @event celldblclick
7732          * Fires when a cell is double clicked
7733          * @param {Roo.bootstrap.Table} this
7734          * @param {Roo.Element} el
7735          * @param {Number} rowIndex
7736          * @param {Number} columnIndex
7737          * @param {Roo.EventObject} e
7738          */
7739         "celldblclick" : true,
7740         /**
7741          * @event rowclick
7742          * Fires when a row is clicked
7743          * @param {Roo.bootstrap.Table} this
7744          * @param {Roo.Element} el
7745          * @param {Number} rowIndex
7746          * @param {Roo.EventObject} e
7747          */
7748         "rowclick" : true,
7749         /**
7750          * @event rowdblclick
7751          * Fires when a row is double clicked
7752          * @param {Roo.bootstrap.Table} this
7753          * @param {Roo.Element} el
7754          * @param {Number} rowIndex
7755          * @param {Roo.EventObject} e
7756          */
7757         "rowdblclick" : true,
7758         /**
7759          * @event mouseover
7760          * Fires when a mouseover occur
7761          * @param {Roo.bootstrap.Table} this
7762          * @param {Roo.Element} el
7763          * @param {Number} rowIndex
7764          * @param {Number} columnIndex
7765          * @param {Roo.EventObject} e
7766          */
7767         "mouseover" : true,
7768         /**
7769          * @event mouseout
7770          * Fires when a mouseout occur
7771          * @param {Roo.bootstrap.Table} this
7772          * @param {Roo.Element} el
7773          * @param {Number} rowIndex
7774          * @param {Number} columnIndex
7775          * @param {Roo.EventObject} e
7776          */
7777         "mouseout" : true,
7778         /**
7779          * @event rowclass
7780          * Fires when a row is rendered, so you can change add a style to it.
7781          * @param {Roo.bootstrap.Table} this
7782          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7783          */
7784         'rowclass' : true,
7785           /**
7786          * @event rowsrendered
7787          * Fires when all the  rows have been rendered
7788          * @param {Roo.bootstrap.Table} this
7789          */
7790         'rowsrendered' : true,
7791         /**
7792          * @event contextmenu
7793          * The raw contextmenu event for the entire grid.
7794          * @param {Roo.EventObject} e
7795          */
7796         "contextmenu" : true,
7797         /**
7798          * @event rowcontextmenu
7799          * Fires when a row is right clicked
7800          * @param {Roo.bootstrap.Table} this
7801          * @param {Number} rowIndex
7802          * @param {Roo.EventObject} e
7803          */
7804         "rowcontextmenu" : true,
7805         /**
7806          * @event cellcontextmenu
7807          * Fires when a cell is right clicked
7808          * @param {Roo.bootstrap.Table} this
7809          * @param {Number} rowIndex
7810          * @param {Number} cellIndex
7811          * @param {Roo.EventObject} e
7812          */
7813          "cellcontextmenu" : true,
7814          /**
7815          * @event headercontextmenu
7816          * Fires when a header is right clicked
7817          * @param {Roo.bootstrap.Table} this
7818          * @param {Number} columnIndex
7819          * @param {Roo.EventObject} e
7820          */
7821         "headercontextmenu" : true
7822     });
7823 };
7824
7825 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7826     
7827     cls: false,
7828     align: false,
7829     bgcolor: false,
7830     border: false,
7831     cellpadding: false,
7832     cellspacing: false,
7833     frame: false,
7834     rules: false,
7835     sortable: false,
7836     summary: false,
7837     width: false,
7838     striped : false,
7839     scrollBody : false,
7840     bordered: false,
7841     hover:  false,
7842     condensed : false,
7843     responsive : false,
7844     sm : false,
7845     cm : false,
7846     store : false,
7847     loadMask : false,
7848     footerShow : true,
7849     headerShow : true,
7850   
7851     rowSelection : false,
7852     cellSelection : false,
7853     layout : false,
7854     
7855     // Roo.Element - the tbody
7856     mainBody: false,
7857     // Roo.Element - thead element
7858     mainHead: false,
7859     
7860     container: false, // used by gridpanel...
7861     
7862     lazyLoad : false,
7863     
7864     CSS : Roo.util.CSS,
7865     
7866     auto_hide_footer : false,
7867     
7868     getAutoCreate : function()
7869     {
7870         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7871         
7872         cfg = {
7873             tag: 'table',
7874             cls : 'table',
7875             cn : []
7876         };
7877         if (this.scrollBody) {
7878             cfg.cls += ' table-body-fixed';
7879         }    
7880         if (this.striped) {
7881             cfg.cls += ' table-striped';
7882         }
7883         
7884         if (this.hover) {
7885             cfg.cls += ' table-hover';
7886         }
7887         if (this.bordered) {
7888             cfg.cls += ' table-bordered';
7889         }
7890         if (this.condensed) {
7891             cfg.cls += ' table-condensed';
7892         }
7893         if (this.responsive) {
7894             cfg.cls += ' table-responsive';
7895         }
7896         
7897         if (this.cls) {
7898             cfg.cls+=  ' ' +this.cls;
7899         }
7900         
7901         // this lot should be simplifed...
7902         var _t = this;
7903         var cp = [
7904             'align',
7905             'bgcolor',
7906             'border',
7907             'cellpadding',
7908             'cellspacing',
7909             'frame',
7910             'rules',
7911             'sortable',
7912             'summary',
7913             'width'
7914         ].forEach(function(k) {
7915             if (_t[k]) {
7916                 cfg[k] = _t[k];
7917             }
7918         });
7919         
7920         
7921         if (this.layout) {
7922             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7923         }
7924         
7925         if(this.store || this.cm){
7926             if(this.headerShow){
7927                 cfg.cn.push(this.renderHeader());
7928             }
7929             
7930             cfg.cn.push(this.renderBody());
7931             
7932             if(this.footerShow){
7933                 cfg.cn.push(this.renderFooter());
7934             }
7935             // where does this come from?
7936             //cfg.cls+=  ' TableGrid';
7937         }
7938         
7939         return { cn : [ cfg ] };
7940     },
7941     
7942     initEvents : function()
7943     {   
7944         if(!this.store || !this.cm){
7945             return;
7946         }
7947         if (this.selModel) {
7948             this.selModel.initEvents();
7949         }
7950         
7951         
7952         //Roo.log('initEvents with ds!!!!');
7953         
7954         this.mainBody = this.el.select('tbody', true).first();
7955         this.mainHead = this.el.select('thead', true).first();
7956         this.mainFoot = this.el.select('tfoot', true).first();
7957         
7958         
7959         
7960         var _this = this;
7961         
7962         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7963             e.on('click', _this.sort, _this);
7964         });
7965         
7966         this.mainBody.on("click", this.onClick, this);
7967         this.mainBody.on("dblclick", this.onDblClick, this);
7968         
7969         // why is this done????? = it breaks dialogs??
7970         //this.parent().el.setStyle('position', 'relative');
7971         
7972         
7973         if (this.footer) {
7974             this.footer.parentId = this.id;
7975             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7976             
7977             if(this.lazyLoad){
7978                 this.el.select('tfoot tr td').first().addClass('hide');
7979             }
7980         } 
7981         
7982         if(this.loadMask) {
7983             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7984         }
7985         
7986         this.store.on('load', this.onLoad, this);
7987         this.store.on('beforeload', this.onBeforeLoad, this);
7988         this.store.on('update', this.onUpdate, this);
7989         this.store.on('add', this.onAdd, this);
7990         this.store.on("clear", this.clear, this);
7991         
7992         this.el.on("contextmenu", this.onContextMenu, this);
7993         
7994         this.mainBody.on('scroll', this.onBodyScroll, this);
7995         
7996         this.cm.on("headerchange", this.onHeaderChange, this);
7997         
7998         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7999         
8000     },
8001     
8002     onContextMenu : function(e, t)
8003     {
8004         this.processEvent("contextmenu", e);
8005     },
8006     
8007     processEvent : function(name, e)
8008     {
8009         if (name != 'touchstart' ) {
8010             this.fireEvent(name, e);    
8011         }
8012         
8013         var t = e.getTarget();
8014         
8015         var cell = Roo.get(t);
8016         
8017         if(!cell){
8018             return;
8019         }
8020         
8021         if(cell.findParent('tfoot', false, true)){
8022             return;
8023         }
8024         
8025         if(cell.findParent('thead', false, true)){
8026             
8027             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8028                 cell = Roo.get(t).findParent('th', false, true);
8029                 if (!cell) {
8030                     Roo.log("failed to find th in thead?");
8031                     Roo.log(e.getTarget());
8032                     return;
8033                 }
8034             }
8035             
8036             var cellIndex = cell.dom.cellIndex;
8037             
8038             var ename = name == 'touchstart' ? 'click' : name;
8039             this.fireEvent("header" + ename, this, cellIndex, e);
8040             
8041             return;
8042         }
8043         
8044         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8045             cell = Roo.get(t).findParent('td', false, true);
8046             if (!cell) {
8047                 Roo.log("failed to find th in tbody?");
8048                 Roo.log(e.getTarget());
8049                 return;
8050             }
8051         }
8052         
8053         var row = cell.findParent('tr', false, true);
8054         var cellIndex = cell.dom.cellIndex;
8055         var rowIndex = row.dom.rowIndex - 1;
8056         
8057         if(row !== false){
8058             
8059             this.fireEvent("row" + name, this, rowIndex, e);
8060             
8061             if(cell !== false){
8062             
8063                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8064             }
8065         }
8066         
8067     },
8068     
8069     onMouseover : function(e, el)
8070     {
8071         var cell = Roo.get(el);
8072         
8073         if(!cell){
8074             return;
8075         }
8076         
8077         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8078             cell = cell.findParent('td', false, true);
8079         }
8080         
8081         var row = cell.findParent('tr', false, true);
8082         var cellIndex = cell.dom.cellIndex;
8083         var rowIndex = row.dom.rowIndex - 1; // start from 0
8084         
8085         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8086         
8087     },
8088     
8089     onMouseout : function(e, el)
8090     {
8091         var cell = Roo.get(el);
8092         
8093         if(!cell){
8094             return;
8095         }
8096         
8097         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8098             cell = cell.findParent('td', false, true);
8099         }
8100         
8101         var row = cell.findParent('tr', false, true);
8102         var cellIndex = cell.dom.cellIndex;
8103         var rowIndex = row.dom.rowIndex - 1; // start from 0
8104         
8105         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8106         
8107     },
8108     
8109     onClick : function(e, el)
8110     {
8111         var cell = Roo.get(el);
8112         
8113         if(!cell || (!this.cellSelection && !this.rowSelection)){
8114             return;
8115         }
8116         
8117         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8118             cell = cell.findParent('td', false, true);
8119         }
8120         
8121         if(!cell || typeof(cell) == 'undefined'){
8122             return;
8123         }
8124         
8125         var row = cell.findParent('tr', false, true);
8126         
8127         if(!row || typeof(row) == 'undefined'){
8128             return;
8129         }
8130         
8131         var cellIndex = cell.dom.cellIndex;
8132         var rowIndex = this.getRowIndex(row);
8133         
8134         // why??? - should these not be based on SelectionModel?
8135         if(this.cellSelection){
8136             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8137         }
8138         
8139         if(this.rowSelection){
8140             this.fireEvent('rowclick', this, row, rowIndex, e);
8141         }
8142         
8143         
8144     },
8145         
8146     onDblClick : function(e,el)
8147     {
8148         var cell = Roo.get(el);
8149         
8150         if(!cell || (!this.cellSelection && !this.rowSelection)){
8151             return;
8152         }
8153         
8154         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8155             cell = cell.findParent('td', false, true);
8156         }
8157         
8158         if(!cell || typeof(cell) == 'undefined'){
8159             return;
8160         }
8161         
8162         var row = cell.findParent('tr', false, true);
8163         
8164         if(!row || typeof(row) == 'undefined'){
8165             return;
8166         }
8167         
8168         var cellIndex = cell.dom.cellIndex;
8169         var rowIndex = this.getRowIndex(row);
8170         
8171         if(this.cellSelection){
8172             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8173         }
8174         
8175         if(this.rowSelection){
8176             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8177         }
8178     },
8179     
8180     sort : function(e,el)
8181     {
8182         var col = Roo.get(el);
8183         
8184         if(!col.hasClass('sortable')){
8185             return;
8186         }
8187         
8188         var sort = col.attr('sort');
8189         var dir = 'ASC';
8190         
8191         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8192             dir = 'DESC';
8193         }
8194         
8195         this.store.sortInfo = {field : sort, direction : dir};
8196         
8197         if (this.footer) {
8198             Roo.log("calling footer first");
8199             this.footer.onClick('first');
8200         } else {
8201         
8202             this.store.load({ params : { start : 0 } });
8203         }
8204     },
8205     
8206     renderHeader : function()
8207     {
8208         var header = {
8209             tag: 'thead',
8210             cn : []
8211         };
8212         
8213         var cm = this.cm;
8214         this.totalWidth = 0;
8215         
8216         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8217             
8218             var config = cm.config[i];
8219             
8220             var c = {
8221                 tag: 'th',
8222                 cls : 'x-hcol-' + i,
8223                 style : '',
8224                 html: cm.getColumnHeader(i)
8225             };
8226             
8227             var hh = '';
8228             
8229             if(typeof(config.sortable) != 'undefined' && config.sortable){
8230                 c.cls = 'sortable';
8231                 c.html = '<i class="glyphicon"></i>' + c.html;
8232             }
8233             
8234             // could use BS4 hidden-..-down 
8235             
8236             if(typeof(config.lgHeader) != 'undefined'){
8237                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8238             }
8239             
8240             if(typeof(config.mdHeader) != 'undefined'){
8241                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8242             }
8243             
8244             if(typeof(config.smHeader) != 'undefined'){
8245                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8246             }
8247             
8248             if(typeof(config.xsHeader) != 'undefined'){
8249                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8250             }
8251             
8252             if(hh.length){
8253                 c.html = hh;
8254             }
8255             
8256             if(typeof(config.tooltip) != 'undefined'){
8257                 c.tooltip = config.tooltip;
8258             }
8259             
8260             if(typeof(config.colspan) != 'undefined'){
8261                 c.colspan = config.colspan;
8262             }
8263             
8264             if(typeof(config.hidden) != 'undefined' && config.hidden){
8265                 c.style += ' display:none;';
8266             }
8267             
8268             if(typeof(config.dataIndex) != 'undefined'){
8269                 c.sort = config.dataIndex;
8270             }
8271             
8272            
8273             
8274             if(typeof(config.align) != 'undefined' && config.align.length){
8275                 c.style += ' text-align:' + config.align + ';';
8276             }
8277             
8278             if(typeof(config.width) != 'undefined'){
8279                 c.style += ' width:' + config.width + 'px;';
8280                 this.totalWidth += config.width;
8281             } else {
8282                 this.totalWidth += 100; // assume minimum of 100 per column?
8283             }
8284             
8285             if(typeof(config.cls) != 'undefined'){
8286                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8287             }
8288             
8289             ['xs','sm','md','lg'].map(function(size){
8290                 
8291                 if(typeof(config[size]) == 'undefined'){
8292                     return;
8293                 }
8294                  
8295                 if (!config[size]) { // 0 = hidden
8296                     // BS 4 '0' is treated as hide that column and below.
8297                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8298                     return;
8299                 }
8300                 
8301                 c.cls += ' col-' + size + '-' + config[size] + (
8302                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8303                 );
8304                 
8305                 
8306             });
8307             
8308             header.cn.push(c)
8309         }
8310         
8311         return header;
8312     },
8313     
8314     renderBody : function()
8315     {
8316         var body = {
8317             tag: 'tbody',
8318             cn : [
8319                 {
8320                     tag: 'tr',
8321                     cn : [
8322                         {
8323                             tag : 'td',
8324                             colspan :  this.cm.getColumnCount()
8325                         }
8326                     ]
8327                 }
8328             ]
8329         };
8330         
8331         return body;
8332     },
8333     
8334     renderFooter : function()
8335     {
8336         var footer = {
8337             tag: 'tfoot',
8338             cn : [
8339                 {
8340                     tag: 'tr',
8341                     cn : [
8342                         {
8343                             tag : 'td',
8344                             colspan :  this.cm.getColumnCount()
8345                         }
8346                     ]
8347                 }
8348             ]
8349         };
8350         
8351         return footer;
8352     },
8353     
8354     
8355     
8356     onLoad : function()
8357     {
8358 //        Roo.log('ds onload');
8359         this.clear();
8360         
8361         var _this = this;
8362         var cm = this.cm;
8363         var ds = this.store;
8364         
8365         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8366             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8367             if (_this.store.sortInfo) {
8368                     
8369                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8370                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8371                 }
8372                 
8373                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8374                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8375                 }
8376             }
8377         });
8378         
8379         var tbody =  this.mainBody;
8380               
8381         if(ds.getCount() > 0){
8382             ds.data.each(function(d,rowIndex){
8383                 var row =  this.renderRow(cm, ds, rowIndex);
8384                 
8385                 tbody.createChild(row);
8386                 
8387                 var _this = this;
8388                 
8389                 if(row.cellObjects.length){
8390                     Roo.each(row.cellObjects, function(r){
8391                         _this.renderCellObject(r);
8392                     })
8393                 }
8394                 
8395             }, this);
8396         }
8397         
8398         var tfoot = this.el.select('tfoot', true).first();
8399         
8400         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8401             
8402             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8403             
8404             var total = this.ds.getTotalCount();
8405             
8406             if(this.footer.pageSize < total){
8407                 this.mainFoot.show();
8408             }
8409         }
8410         
8411         Roo.each(this.el.select('tbody td', true).elements, function(e){
8412             e.on('mouseover', _this.onMouseover, _this);
8413         });
8414         
8415         Roo.each(this.el.select('tbody td', true).elements, function(e){
8416             e.on('mouseout', _this.onMouseout, _this);
8417         });
8418         this.fireEvent('rowsrendered', this);
8419         
8420         this.autoSize();
8421     },
8422     
8423     
8424     onUpdate : function(ds,record)
8425     {
8426         this.refreshRow(record);
8427         this.autoSize();
8428     },
8429     
8430     onRemove : function(ds, record, index, isUpdate){
8431         if(isUpdate !== true){
8432             this.fireEvent("beforerowremoved", this, index, record);
8433         }
8434         var bt = this.mainBody.dom;
8435         
8436         var rows = this.el.select('tbody > tr', true).elements;
8437         
8438         if(typeof(rows[index]) != 'undefined'){
8439             bt.removeChild(rows[index].dom);
8440         }
8441         
8442 //        if(bt.rows[index]){
8443 //            bt.removeChild(bt.rows[index]);
8444 //        }
8445         
8446         if(isUpdate !== true){
8447             //this.stripeRows(index);
8448             //this.syncRowHeights(index, index);
8449             //this.layout();
8450             this.fireEvent("rowremoved", this, index, record);
8451         }
8452     },
8453     
8454     onAdd : function(ds, records, rowIndex)
8455     {
8456         //Roo.log('on Add called');
8457         // - note this does not handle multiple adding very well..
8458         var bt = this.mainBody.dom;
8459         for (var i =0 ; i < records.length;i++) {
8460             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8461             //Roo.log(records[i]);
8462             //Roo.log(this.store.getAt(rowIndex+i));
8463             this.insertRow(this.store, rowIndex + i, false);
8464             return;
8465         }
8466         
8467     },
8468     
8469     
8470     refreshRow : function(record){
8471         var ds = this.store, index;
8472         if(typeof record == 'number'){
8473             index = record;
8474             record = ds.getAt(index);
8475         }else{
8476             index = ds.indexOf(record);
8477             if (index < 0) {
8478                 return; // should not happen - but seems to 
8479             }
8480         }
8481         this.insertRow(ds, index, true);
8482         this.autoSize();
8483         this.onRemove(ds, record, index+1, true);
8484         this.autoSize();
8485         //this.syncRowHeights(index, index);
8486         //this.layout();
8487         this.fireEvent("rowupdated", this, index, record);
8488     },
8489     
8490     insertRow : function(dm, rowIndex, isUpdate){
8491         
8492         if(!isUpdate){
8493             this.fireEvent("beforerowsinserted", this, rowIndex);
8494         }
8495             //var s = this.getScrollState();
8496         var row = this.renderRow(this.cm, this.store, rowIndex);
8497         // insert before rowIndex..
8498         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8499         
8500         var _this = this;
8501                 
8502         if(row.cellObjects.length){
8503             Roo.each(row.cellObjects, function(r){
8504                 _this.renderCellObject(r);
8505             })
8506         }
8507             
8508         if(!isUpdate){
8509             this.fireEvent("rowsinserted", this, rowIndex);
8510             //this.syncRowHeights(firstRow, lastRow);
8511             //this.stripeRows(firstRow);
8512             //this.layout();
8513         }
8514         
8515     },
8516     
8517     
8518     getRowDom : function(rowIndex)
8519     {
8520         var rows = this.el.select('tbody > tr', true).elements;
8521         
8522         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8523         
8524     },
8525     // returns the object tree for a tr..
8526   
8527     
8528     renderRow : function(cm, ds, rowIndex) 
8529     {
8530         var d = ds.getAt(rowIndex);
8531         
8532         var row = {
8533             tag : 'tr',
8534             cls : 'x-row-' + rowIndex,
8535             cn : []
8536         };
8537             
8538         var cellObjects = [];
8539         
8540         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8541             var config = cm.config[i];
8542             
8543             var renderer = cm.getRenderer(i);
8544             var value = '';
8545             var id = false;
8546             
8547             if(typeof(renderer) !== 'undefined'){
8548                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8549             }
8550             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8551             // and are rendered into the cells after the row is rendered - using the id for the element.
8552             
8553             if(typeof(value) === 'object'){
8554                 id = Roo.id();
8555                 cellObjects.push({
8556                     container : id,
8557                     cfg : value 
8558                 })
8559             }
8560             
8561             var rowcfg = {
8562                 record: d,
8563                 rowIndex : rowIndex,
8564                 colIndex : i,
8565                 rowClass : ''
8566             };
8567
8568             this.fireEvent('rowclass', this, rowcfg);
8569             
8570             var td = {
8571                 tag: 'td',
8572                 cls : rowcfg.rowClass + ' x-col-' + i,
8573                 style: '',
8574                 html: (typeof(value) === 'object') ? '' : value
8575             };
8576             
8577             if (id) {
8578                 td.id = id;
8579             }
8580             
8581             if(typeof(config.colspan) != 'undefined'){
8582                 td.colspan = config.colspan;
8583             }
8584             
8585             if(typeof(config.hidden) != 'undefined' && config.hidden){
8586                 td.style += ' display:none;';
8587             }
8588             
8589             if(typeof(config.align) != 'undefined' && config.align.length){
8590                 td.style += ' text-align:' + config.align + ';';
8591             }
8592             if(typeof(config.valign) != 'undefined' && config.valign.length){
8593                 td.style += ' vertical-align:' + config.valign + ';';
8594             }
8595             
8596             if(typeof(config.width) != 'undefined'){
8597                 td.style += ' width:' +  config.width + 'px;';
8598             }
8599             
8600             if(typeof(config.cursor) != 'undefined'){
8601                 td.style += ' cursor:' +  config.cursor + ';';
8602             }
8603             
8604             if(typeof(config.cls) != 'undefined'){
8605                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8606             }
8607             
8608             ['xs','sm','md','lg'].map(function(size){
8609                 
8610                 if(typeof(config[size]) == 'undefined'){
8611                     return;
8612                 }
8613                 
8614                 
8615                   
8616                 if (!config[size]) { // 0 = hidden
8617                     // BS 4 '0' is treated as hide that column and below.
8618                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8619                     return;
8620                 }
8621                 
8622                 td.cls += ' col-' + size + '-' + config[size] + (
8623                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8624                 );
8625                  
8626
8627             });
8628             
8629             row.cn.push(td);
8630            
8631         }
8632         
8633         row.cellObjects = cellObjects;
8634         
8635         return row;
8636           
8637     },
8638     
8639     
8640     
8641     onBeforeLoad : function()
8642     {
8643         
8644     },
8645      /**
8646      * Remove all rows
8647      */
8648     clear : function()
8649     {
8650         this.el.select('tbody', true).first().dom.innerHTML = '';
8651     },
8652     /**
8653      * Show or hide a row.
8654      * @param {Number} rowIndex to show or hide
8655      * @param {Boolean} state hide
8656      */
8657     setRowVisibility : function(rowIndex, state)
8658     {
8659         var bt = this.mainBody.dom;
8660         
8661         var rows = this.el.select('tbody > tr', true).elements;
8662         
8663         if(typeof(rows[rowIndex]) == 'undefined'){
8664             return;
8665         }
8666         rows[rowIndex].dom.style.display = state ? '' : 'none';
8667     },
8668     
8669     
8670     getSelectionModel : function(){
8671         if(!this.selModel){
8672             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8673         }
8674         return this.selModel;
8675     },
8676     /*
8677      * Render the Roo.bootstrap object from renderder
8678      */
8679     renderCellObject : function(r)
8680     {
8681         var _this = this;
8682         
8683         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8684         
8685         var t = r.cfg.render(r.container);
8686         
8687         if(r.cfg.cn){
8688             Roo.each(r.cfg.cn, function(c){
8689                 var child = {
8690                     container: t.getChildContainer(),
8691                     cfg: c
8692                 };
8693                 _this.renderCellObject(child);
8694             })
8695         }
8696     },
8697     
8698     getRowIndex : function(row)
8699     {
8700         var rowIndex = -1;
8701         
8702         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8703             if(el != row){
8704                 return;
8705             }
8706             
8707             rowIndex = index;
8708         });
8709         
8710         return rowIndex;
8711     },
8712      /**
8713      * Returns the grid's underlying element = used by panel.Grid
8714      * @return {Element} The element
8715      */
8716     getGridEl : function(){
8717         return this.el;
8718     },
8719      /**
8720      * Forces a resize - used by panel.Grid
8721      * @return {Element} The element
8722      */
8723     autoSize : function()
8724     {
8725         //var ctr = Roo.get(this.container.dom.parentElement);
8726         var ctr = Roo.get(this.el.dom);
8727         
8728         var thd = this.getGridEl().select('thead',true).first();
8729         var tbd = this.getGridEl().select('tbody', true).first();
8730         var tfd = this.getGridEl().select('tfoot', true).first();
8731         
8732         var cw = ctr.getWidth();
8733         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8734         
8735         if (tbd) {
8736             
8737             tbd.setWidth(ctr.getWidth());
8738             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8739             // this needs fixing for various usage - currently only hydra job advers I think..
8740             //tdb.setHeight(
8741             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8742             //); 
8743             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8744             cw -= barsize;
8745         }
8746         cw = Math.max(cw, this.totalWidth);
8747         this.getGridEl().select('tbody tr',true).setWidth(cw);
8748         
8749         // resize 'expandable coloumn?
8750         
8751         return; // we doe not have a view in this design..
8752         
8753     },
8754     onBodyScroll: function()
8755     {
8756         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8757         if(this.mainHead){
8758             this.mainHead.setStyle({
8759                 'position' : 'relative',
8760                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8761             });
8762         }
8763         
8764         if(this.lazyLoad){
8765             
8766             var scrollHeight = this.mainBody.dom.scrollHeight;
8767             
8768             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8769             
8770             var height = this.mainBody.getHeight();
8771             
8772             if(scrollHeight - height == scrollTop) {
8773                 
8774                 var total = this.ds.getTotalCount();
8775                 
8776                 if(this.footer.cursor + this.footer.pageSize < total){
8777                     
8778                     this.footer.ds.load({
8779                         params : {
8780                             start : this.footer.cursor + this.footer.pageSize,
8781                             limit : this.footer.pageSize
8782                         },
8783                         add : true
8784                     });
8785                 }
8786             }
8787             
8788         }
8789     },
8790     
8791     onHeaderChange : function()
8792     {
8793         var header = this.renderHeader();
8794         var table = this.el.select('table', true).first();
8795         
8796         this.mainHead.remove();
8797         this.mainHead = table.createChild(header, this.mainBody, false);
8798     },
8799     
8800     onHiddenChange : function(colModel, colIndex, hidden)
8801     {
8802         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8803         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8804         
8805         this.CSS.updateRule(thSelector, "display", "");
8806         this.CSS.updateRule(tdSelector, "display", "");
8807         
8808         if(hidden){
8809             this.CSS.updateRule(thSelector, "display", "none");
8810             this.CSS.updateRule(tdSelector, "display", "none");
8811         }
8812         
8813         this.onHeaderChange();
8814         this.onLoad();
8815     },
8816     
8817     setColumnWidth: function(col_index, width)
8818     {
8819         // width = "md-2 xs-2..."
8820         if(!this.colModel.config[col_index]) {
8821             return;
8822         }
8823         
8824         var w = width.split(" ");
8825         
8826         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8827         
8828         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8829         
8830         
8831         for(var j = 0; j < w.length; j++) {
8832             
8833             if(!w[j]) {
8834                 continue;
8835             }
8836             
8837             var size_cls = w[j].split("-");
8838             
8839             if(!Number.isInteger(size_cls[1] * 1)) {
8840                 continue;
8841             }
8842             
8843             if(!this.colModel.config[col_index][size_cls[0]]) {
8844                 continue;
8845             }
8846             
8847             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8848                 continue;
8849             }
8850             
8851             h_row[0].classList.replace(
8852                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8853                 "col-"+size_cls[0]+"-"+size_cls[1]
8854             );
8855             
8856             for(var i = 0; i < rows.length; i++) {
8857                 
8858                 var size_cls = w[j].split("-");
8859                 
8860                 if(!Number.isInteger(size_cls[1] * 1)) {
8861                     continue;
8862                 }
8863                 
8864                 if(!this.colModel.config[col_index][size_cls[0]]) {
8865                     continue;
8866                 }
8867                 
8868                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8869                     continue;
8870                 }
8871                 
8872                 rows[i].classList.replace(
8873                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8874                     "col-"+size_cls[0]+"-"+size_cls[1]
8875                 );
8876             }
8877             
8878             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8879         }
8880     }
8881 });
8882
8883  
8884
8885  /*
8886  * - LGPL
8887  *
8888  * table cell
8889  * 
8890  */
8891
8892 /**
8893  * @class Roo.bootstrap.TableCell
8894  * @extends Roo.bootstrap.Component
8895  * Bootstrap TableCell class
8896  * @cfg {String} html cell contain text
8897  * @cfg {String} cls cell class
8898  * @cfg {String} tag cell tag (td|th) default td
8899  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8900  * @cfg {String} align Aligns the content in a cell
8901  * @cfg {String} axis Categorizes cells
8902  * @cfg {String} bgcolor Specifies the background color of a cell
8903  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8904  * @cfg {Number} colspan Specifies the number of columns a cell should span
8905  * @cfg {String} headers Specifies one or more header cells a cell is related to
8906  * @cfg {Number} height Sets the height of a cell
8907  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8908  * @cfg {Number} rowspan Sets the number of rows a cell should span
8909  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8910  * @cfg {String} valign Vertical aligns the content in a cell
8911  * @cfg {Number} width Specifies the width of a cell
8912  * 
8913  * @constructor
8914  * Create a new TableCell
8915  * @param {Object} config The config object
8916  */
8917
8918 Roo.bootstrap.TableCell = function(config){
8919     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8920 };
8921
8922 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8923     
8924     html: false,
8925     cls: false,
8926     tag: false,
8927     abbr: false,
8928     align: false,
8929     axis: false,
8930     bgcolor: false,
8931     charoff: false,
8932     colspan: false,
8933     headers: false,
8934     height: false,
8935     nowrap: false,
8936     rowspan: false,
8937     scope: false,
8938     valign: false,
8939     width: false,
8940     
8941     
8942     getAutoCreate : function(){
8943         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8944         
8945         cfg = {
8946             tag: 'td'
8947         };
8948         
8949         if(this.tag){
8950             cfg.tag = this.tag;
8951         }
8952         
8953         if (this.html) {
8954             cfg.html=this.html
8955         }
8956         if (this.cls) {
8957             cfg.cls=this.cls
8958         }
8959         if (this.abbr) {
8960             cfg.abbr=this.abbr
8961         }
8962         if (this.align) {
8963             cfg.align=this.align
8964         }
8965         if (this.axis) {
8966             cfg.axis=this.axis
8967         }
8968         if (this.bgcolor) {
8969             cfg.bgcolor=this.bgcolor
8970         }
8971         if (this.charoff) {
8972             cfg.charoff=this.charoff
8973         }
8974         if (this.colspan) {
8975             cfg.colspan=this.colspan
8976         }
8977         if (this.headers) {
8978             cfg.headers=this.headers
8979         }
8980         if (this.height) {
8981             cfg.height=this.height
8982         }
8983         if (this.nowrap) {
8984             cfg.nowrap=this.nowrap
8985         }
8986         if (this.rowspan) {
8987             cfg.rowspan=this.rowspan
8988         }
8989         if (this.scope) {
8990             cfg.scope=this.scope
8991         }
8992         if (this.valign) {
8993             cfg.valign=this.valign
8994         }
8995         if (this.width) {
8996             cfg.width=this.width
8997         }
8998         
8999         
9000         return cfg;
9001     }
9002    
9003 });
9004
9005  
9006
9007  /*
9008  * - LGPL
9009  *
9010  * table row
9011  * 
9012  */
9013
9014 /**
9015  * @class Roo.bootstrap.TableRow
9016  * @extends Roo.bootstrap.Component
9017  * Bootstrap TableRow class
9018  * @cfg {String} cls row class
9019  * @cfg {String} align Aligns the content in a table row
9020  * @cfg {String} bgcolor Specifies a background color for a table row
9021  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9022  * @cfg {String} valign Vertical aligns the content in a table row
9023  * 
9024  * @constructor
9025  * Create a new TableRow
9026  * @param {Object} config The config object
9027  */
9028
9029 Roo.bootstrap.TableRow = function(config){
9030     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9031 };
9032
9033 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9034     
9035     cls: false,
9036     align: false,
9037     bgcolor: false,
9038     charoff: false,
9039     valign: false,
9040     
9041     getAutoCreate : function(){
9042         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9043         
9044         cfg = {
9045             tag: 'tr'
9046         };
9047             
9048         if(this.cls){
9049             cfg.cls = this.cls;
9050         }
9051         if(this.align){
9052             cfg.align = this.align;
9053         }
9054         if(this.bgcolor){
9055             cfg.bgcolor = this.bgcolor;
9056         }
9057         if(this.charoff){
9058             cfg.charoff = this.charoff;
9059         }
9060         if(this.valign){
9061             cfg.valign = this.valign;
9062         }
9063         
9064         return cfg;
9065     }
9066    
9067 });
9068
9069  
9070
9071  /*
9072  * - LGPL
9073  *
9074  * table body
9075  * 
9076  */
9077
9078 /**
9079  * @class Roo.bootstrap.TableBody
9080  * @extends Roo.bootstrap.Component
9081  * Bootstrap TableBody class
9082  * @cfg {String} cls element class
9083  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9084  * @cfg {String} align Aligns the content inside the element
9085  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9086  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9087  * 
9088  * @constructor
9089  * Create a new TableBody
9090  * @param {Object} config The config object
9091  */
9092
9093 Roo.bootstrap.TableBody = function(config){
9094     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9095 };
9096
9097 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9098     
9099     cls: false,
9100     tag: false,
9101     align: false,
9102     charoff: false,
9103     valign: false,
9104     
9105     getAutoCreate : function(){
9106         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9107         
9108         cfg = {
9109             tag: 'tbody'
9110         };
9111             
9112         if (this.cls) {
9113             cfg.cls=this.cls
9114         }
9115         if(this.tag){
9116             cfg.tag = this.tag;
9117         }
9118         
9119         if(this.align){
9120             cfg.align = this.align;
9121         }
9122         if(this.charoff){
9123             cfg.charoff = this.charoff;
9124         }
9125         if(this.valign){
9126             cfg.valign = this.valign;
9127         }
9128         
9129         return cfg;
9130     }
9131     
9132     
9133 //    initEvents : function()
9134 //    {
9135 //        
9136 //        if(!this.store){
9137 //            return;
9138 //        }
9139 //        
9140 //        this.store = Roo.factory(this.store, Roo.data);
9141 //        this.store.on('load', this.onLoad, this);
9142 //        
9143 //        this.store.load();
9144 //        
9145 //    },
9146 //    
9147 //    onLoad: function () 
9148 //    {   
9149 //        this.fireEvent('load', this);
9150 //    }
9151 //    
9152 //   
9153 });
9154
9155  
9156
9157  /*
9158  * Based on:
9159  * Ext JS Library 1.1.1
9160  * Copyright(c) 2006-2007, Ext JS, LLC.
9161  *
9162  * Originally Released Under LGPL - original licence link has changed is not relivant.
9163  *
9164  * Fork - LGPL
9165  * <script type="text/javascript">
9166  */
9167
9168 // as we use this in bootstrap.
9169 Roo.namespace('Roo.form');
9170  /**
9171  * @class Roo.form.Action
9172  * Internal Class used to handle form actions
9173  * @constructor
9174  * @param {Roo.form.BasicForm} el The form element or its id
9175  * @param {Object} config Configuration options
9176  */
9177
9178  
9179  
9180 // define the action interface
9181 Roo.form.Action = function(form, options){
9182     this.form = form;
9183     this.options = options || {};
9184 };
9185 /**
9186  * Client Validation Failed
9187  * @const 
9188  */
9189 Roo.form.Action.CLIENT_INVALID = 'client';
9190 /**
9191  * Server Validation Failed
9192  * @const 
9193  */
9194 Roo.form.Action.SERVER_INVALID = 'server';
9195  /**
9196  * Connect to Server Failed
9197  * @const 
9198  */
9199 Roo.form.Action.CONNECT_FAILURE = 'connect';
9200 /**
9201  * Reading Data from Server Failed
9202  * @const 
9203  */
9204 Roo.form.Action.LOAD_FAILURE = 'load';
9205
9206 Roo.form.Action.prototype = {
9207     type : 'default',
9208     failureType : undefined,
9209     response : undefined,
9210     result : undefined,
9211
9212     // interface method
9213     run : function(options){
9214
9215     },
9216
9217     // interface method
9218     success : function(response){
9219
9220     },
9221
9222     // interface method
9223     handleResponse : function(response){
9224
9225     },
9226
9227     // default connection failure
9228     failure : function(response){
9229         
9230         this.response = response;
9231         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9232         this.form.afterAction(this, false);
9233     },
9234
9235     processResponse : function(response){
9236         this.response = response;
9237         if(!response.responseText){
9238             return true;
9239         }
9240         this.result = this.handleResponse(response);
9241         return this.result;
9242     },
9243
9244     // utility functions used internally
9245     getUrl : function(appendParams){
9246         var url = this.options.url || this.form.url || this.form.el.dom.action;
9247         if(appendParams){
9248             var p = this.getParams();
9249             if(p){
9250                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9251             }
9252         }
9253         return url;
9254     },
9255
9256     getMethod : function(){
9257         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9258     },
9259
9260     getParams : function(){
9261         var bp = this.form.baseParams;
9262         var p = this.options.params;
9263         if(p){
9264             if(typeof p == "object"){
9265                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9266             }else if(typeof p == 'string' && bp){
9267                 p += '&' + Roo.urlEncode(bp);
9268             }
9269         }else if(bp){
9270             p = Roo.urlEncode(bp);
9271         }
9272         return p;
9273     },
9274
9275     createCallback : function(){
9276         return {
9277             success: this.success,
9278             failure: this.failure,
9279             scope: this,
9280             timeout: (this.form.timeout*1000),
9281             upload: this.form.fileUpload ? this.success : undefined
9282         };
9283     }
9284 };
9285
9286 Roo.form.Action.Submit = function(form, options){
9287     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9288 };
9289
9290 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9291     type : 'submit',
9292
9293     haveProgress : false,
9294     uploadComplete : false,
9295     
9296     // uploadProgress indicator.
9297     uploadProgress : function()
9298     {
9299         if (!this.form.progressUrl) {
9300             return;
9301         }
9302         
9303         if (!this.haveProgress) {
9304             Roo.MessageBox.progress("Uploading", "Uploading");
9305         }
9306         if (this.uploadComplete) {
9307            Roo.MessageBox.hide();
9308            return;
9309         }
9310         
9311         this.haveProgress = true;
9312    
9313         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9314         
9315         var c = new Roo.data.Connection();
9316         c.request({
9317             url : this.form.progressUrl,
9318             params: {
9319                 id : uid
9320             },
9321             method: 'GET',
9322             success : function(req){
9323                //console.log(data);
9324                 var rdata = false;
9325                 var edata;
9326                 try  {
9327                    rdata = Roo.decode(req.responseText)
9328                 } catch (e) {
9329                     Roo.log("Invalid data from server..");
9330                     Roo.log(edata);
9331                     return;
9332                 }
9333                 if (!rdata || !rdata.success) {
9334                     Roo.log(rdata);
9335                     Roo.MessageBox.alert(Roo.encode(rdata));
9336                     return;
9337                 }
9338                 var data = rdata.data;
9339                 
9340                 if (this.uploadComplete) {
9341                    Roo.MessageBox.hide();
9342                    return;
9343                 }
9344                    
9345                 if (data){
9346                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9347                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9348                     );
9349                 }
9350                 this.uploadProgress.defer(2000,this);
9351             },
9352        
9353             failure: function(data) {
9354                 Roo.log('progress url failed ');
9355                 Roo.log(data);
9356             },
9357             scope : this
9358         });
9359            
9360     },
9361     
9362     
9363     run : function()
9364     {
9365         // run get Values on the form, so it syncs any secondary forms.
9366         this.form.getValues();
9367         
9368         var o = this.options;
9369         var method = this.getMethod();
9370         var isPost = method == 'POST';
9371         if(o.clientValidation === false || this.form.isValid()){
9372             
9373             if (this.form.progressUrl) {
9374                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9375                     (new Date() * 1) + '' + Math.random());
9376                     
9377             } 
9378             
9379             
9380             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9381                 form:this.form.el.dom,
9382                 url:this.getUrl(!isPost),
9383                 method: method,
9384                 params:isPost ? this.getParams() : null,
9385                 isUpload: this.form.fileUpload,
9386                 formData : this.form.formData
9387             }));
9388             
9389             this.uploadProgress();
9390
9391         }else if (o.clientValidation !== false){ // client validation failed
9392             this.failureType = Roo.form.Action.CLIENT_INVALID;
9393             this.form.afterAction(this, false);
9394         }
9395     },
9396
9397     success : function(response)
9398     {
9399         this.uploadComplete= true;
9400         if (this.haveProgress) {
9401             Roo.MessageBox.hide();
9402         }
9403         
9404         
9405         var result = this.processResponse(response);
9406         if(result === true || result.success){
9407             this.form.afterAction(this, true);
9408             return;
9409         }
9410         if(result.errors){
9411             this.form.markInvalid(result.errors);
9412             this.failureType = Roo.form.Action.SERVER_INVALID;
9413         }
9414         this.form.afterAction(this, false);
9415     },
9416     failure : function(response)
9417     {
9418         this.uploadComplete= true;
9419         if (this.haveProgress) {
9420             Roo.MessageBox.hide();
9421         }
9422         
9423         this.response = response;
9424         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9425         this.form.afterAction(this, false);
9426     },
9427     
9428     handleResponse : function(response){
9429         if(this.form.errorReader){
9430             var rs = this.form.errorReader.read(response);
9431             var errors = [];
9432             if(rs.records){
9433                 for(var i = 0, len = rs.records.length; i < len; i++) {
9434                     var r = rs.records[i];
9435                     errors[i] = r.data;
9436                 }
9437             }
9438             if(errors.length < 1){
9439                 errors = null;
9440             }
9441             return {
9442                 success : rs.success,
9443                 errors : errors
9444             };
9445         }
9446         var ret = false;
9447         try {
9448             ret = Roo.decode(response.responseText);
9449         } catch (e) {
9450             ret = {
9451                 success: false,
9452                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9453                 errors : []
9454             };
9455         }
9456         return ret;
9457         
9458     }
9459 });
9460
9461
9462 Roo.form.Action.Load = function(form, options){
9463     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9464     this.reader = this.form.reader;
9465 };
9466
9467 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9468     type : 'load',
9469
9470     run : function(){
9471         
9472         Roo.Ajax.request(Roo.apply(
9473                 this.createCallback(), {
9474                     method:this.getMethod(),
9475                     url:this.getUrl(false),
9476                     params:this.getParams()
9477         }));
9478     },
9479
9480     success : function(response){
9481         
9482         var result = this.processResponse(response);
9483         if(result === true || !result.success || !result.data){
9484             this.failureType = Roo.form.Action.LOAD_FAILURE;
9485             this.form.afterAction(this, false);
9486             return;
9487         }
9488         this.form.clearInvalid();
9489         this.form.setValues(result.data);
9490         this.form.afterAction(this, true);
9491     },
9492
9493     handleResponse : function(response){
9494         if(this.form.reader){
9495             var rs = this.form.reader.read(response);
9496             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9497             return {
9498                 success : rs.success,
9499                 data : data
9500             };
9501         }
9502         return Roo.decode(response.responseText);
9503     }
9504 });
9505
9506 Roo.form.Action.ACTION_TYPES = {
9507     'load' : Roo.form.Action.Load,
9508     'submit' : Roo.form.Action.Submit
9509 };/*
9510  * - LGPL
9511  *
9512  * form
9513  *
9514  */
9515
9516 /**
9517  * @class Roo.bootstrap.Form
9518  * @extends Roo.bootstrap.Component
9519  * Bootstrap Form class
9520  * @cfg {String} method  GET | POST (default POST)
9521  * @cfg {String} labelAlign top | left (default top)
9522  * @cfg {String} align left  | right - for navbars
9523  * @cfg {Boolean} loadMask load mask when submit (default true)
9524
9525  *
9526  * @constructor
9527  * Create a new Form
9528  * @param {Object} config The config object
9529  */
9530
9531
9532 Roo.bootstrap.Form = function(config){
9533     
9534     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9535     
9536     Roo.bootstrap.Form.popover.apply();
9537     
9538     this.addEvents({
9539         /**
9540          * @event clientvalidation
9541          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9542          * @param {Form} this
9543          * @param {Boolean} valid true if the form has passed client-side validation
9544          */
9545         clientvalidation: true,
9546         /**
9547          * @event beforeaction
9548          * Fires before any action is performed. Return false to cancel the action.
9549          * @param {Form} this
9550          * @param {Action} action The action to be performed
9551          */
9552         beforeaction: true,
9553         /**
9554          * @event actionfailed
9555          * Fires when an action fails.
9556          * @param {Form} this
9557          * @param {Action} action The action that failed
9558          */
9559         actionfailed : true,
9560         /**
9561          * @event actioncomplete
9562          * Fires when an action is completed.
9563          * @param {Form} this
9564          * @param {Action} action The action that completed
9565          */
9566         actioncomplete : true
9567     });
9568 };
9569
9570 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9571
9572      /**
9573      * @cfg {String} method
9574      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9575      */
9576     method : 'POST',
9577     /**
9578      * @cfg {String} url
9579      * The URL to use for form actions if one isn't supplied in the action options.
9580      */
9581     /**
9582      * @cfg {Boolean} fileUpload
9583      * Set to true if this form is a file upload.
9584      */
9585
9586     /**
9587      * @cfg {Object} baseParams
9588      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9589      */
9590
9591     /**
9592      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9593      */
9594     timeout: 30,
9595     /**
9596      * @cfg {Sting} align (left|right) for navbar forms
9597      */
9598     align : 'left',
9599
9600     // private
9601     activeAction : null,
9602
9603     /**
9604      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9605      * element by passing it or its id or mask the form itself by passing in true.
9606      * @type Mixed
9607      */
9608     waitMsgTarget : false,
9609
9610     loadMask : true,
9611     
9612     /**
9613      * @cfg {Boolean} errorMask (true|false) default false
9614      */
9615     errorMask : false,
9616     
9617     /**
9618      * @cfg {Number} maskOffset Default 100
9619      */
9620     maskOffset : 100,
9621     
9622     /**
9623      * @cfg {Boolean} maskBody
9624      */
9625     maskBody : false,
9626
9627     getAutoCreate : function(){
9628
9629         var cfg = {
9630             tag: 'form',
9631             method : this.method || 'POST',
9632             id : this.id || Roo.id(),
9633             cls : ''
9634         };
9635         if (this.parent().xtype.match(/^Nav/)) {
9636             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9637
9638         }
9639
9640         if (this.labelAlign == 'left' ) {
9641             cfg.cls += ' form-horizontal';
9642         }
9643
9644
9645         return cfg;
9646     },
9647     initEvents : function()
9648     {
9649         this.el.on('submit', this.onSubmit, this);
9650         // this was added as random key presses on the form where triggering form submit.
9651         this.el.on('keypress', function(e) {
9652             if (e.getCharCode() != 13) {
9653                 return true;
9654             }
9655             // we might need to allow it for textareas.. and some other items.
9656             // check e.getTarget().
9657
9658             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9659                 return true;
9660             }
9661
9662             Roo.log("keypress blocked");
9663
9664             e.preventDefault();
9665             return false;
9666         });
9667         
9668     },
9669     // private
9670     onSubmit : function(e){
9671         e.stopEvent();
9672     },
9673
9674      /**
9675      * Returns true if client-side validation on the form is successful.
9676      * @return Boolean
9677      */
9678     isValid : function(){
9679         var items = this.getItems();
9680         var valid = true;
9681         var target = false;
9682         
9683         items.each(function(f){
9684             
9685             if(f.validate()){
9686                 return;
9687             }
9688             
9689             Roo.log('invalid field: ' + f.name);
9690             
9691             valid = false;
9692
9693             if(!target && f.el.isVisible(true)){
9694                 target = f;
9695             }
9696            
9697         });
9698         
9699         if(this.errorMask && !valid){
9700             Roo.bootstrap.Form.popover.mask(this, target);
9701         }
9702         
9703         return valid;
9704     },
9705     
9706     /**
9707      * Returns true if any fields in this form have changed since their original load.
9708      * @return Boolean
9709      */
9710     isDirty : function(){
9711         var dirty = false;
9712         var items = this.getItems();
9713         items.each(function(f){
9714            if(f.isDirty()){
9715                dirty = true;
9716                return false;
9717            }
9718            return true;
9719         });
9720         return dirty;
9721     },
9722      /**
9723      * Performs a predefined action (submit or load) or custom actions you define on this form.
9724      * @param {String} actionName The name of the action type
9725      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9726      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9727      * accept other config options):
9728      * <pre>
9729 Property          Type             Description
9730 ----------------  ---------------  ----------------------------------------------------------------------------------
9731 url               String           The url for the action (defaults to the form's url)
9732 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9733 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9734 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9735                                    validate the form on the client (defaults to false)
9736      * </pre>
9737      * @return {BasicForm} this
9738      */
9739     doAction : function(action, options){
9740         if(typeof action == 'string'){
9741             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9742         }
9743         if(this.fireEvent('beforeaction', this, action) !== false){
9744             this.beforeAction(action);
9745             action.run.defer(100, action);
9746         }
9747         return this;
9748     },
9749
9750     // private
9751     beforeAction : function(action){
9752         var o = action.options;
9753         
9754         if(this.loadMask){
9755             
9756             if(this.maskBody){
9757                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9758             } else {
9759                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9760             }
9761         }
9762         // not really supported yet.. ??
9763
9764         //if(this.waitMsgTarget === true){
9765         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9766         //}else if(this.waitMsgTarget){
9767         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9768         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9769         //}else {
9770         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9771        // }
9772
9773     },
9774
9775     // private
9776     afterAction : function(action, success){
9777         this.activeAction = null;
9778         var o = action.options;
9779
9780         if(this.loadMask){
9781             
9782             if(this.maskBody){
9783                 Roo.get(document.body).unmask();
9784             } else {
9785                 this.el.unmask();
9786             }
9787         }
9788         
9789         //if(this.waitMsgTarget === true){
9790 //            this.el.unmask();
9791         //}else if(this.waitMsgTarget){
9792         //    this.waitMsgTarget.unmask();
9793         //}else{
9794         //    Roo.MessageBox.updateProgress(1);
9795         //    Roo.MessageBox.hide();
9796        // }
9797         //
9798         if(success){
9799             if(o.reset){
9800                 this.reset();
9801             }
9802             Roo.callback(o.success, o.scope, [this, action]);
9803             this.fireEvent('actioncomplete', this, action);
9804
9805         }else{
9806
9807             // failure condition..
9808             // we have a scenario where updates need confirming.
9809             // eg. if a locking scenario exists..
9810             // we look for { errors : { needs_confirm : true }} in the response.
9811             if (
9812                 (typeof(action.result) != 'undefined')  &&
9813                 (typeof(action.result.errors) != 'undefined')  &&
9814                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9815            ){
9816                 var _t = this;
9817                 Roo.log("not supported yet");
9818                  /*
9819
9820                 Roo.MessageBox.confirm(
9821                     "Change requires confirmation",
9822                     action.result.errorMsg,
9823                     function(r) {
9824                         if (r != 'yes') {
9825                             return;
9826                         }
9827                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9828                     }
9829
9830                 );
9831                 */
9832
9833
9834                 return;
9835             }
9836
9837             Roo.callback(o.failure, o.scope, [this, action]);
9838             // show an error message if no failed handler is set..
9839             if (!this.hasListener('actionfailed')) {
9840                 Roo.log("need to add dialog support");
9841                 /*
9842                 Roo.MessageBox.alert("Error",
9843                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9844                         action.result.errorMsg :
9845                         "Saving Failed, please check your entries or try again"
9846                 );
9847                 */
9848             }
9849
9850             this.fireEvent('actionfailed', this, action);
9851         }
9852
9853     },
9854     /**
9855      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9856      * @param {String} id The value to search for
9857      * @return Field
9858      */
9859     findField : function(id){
9860         var items = this.getItems();
9861         var field = items.get(id);
9862         if(!field){
9863              items.each(function(f){
9864                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9865                     field = f;
9866                     return false;
9867                 }
9868                 return true;
9869             });
9870         }
9871         return field || null;
9872     },
9873      /**
9874      * Mark fields in this form invalid in bulk.
9875      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9876      * @return {BasicForm} this
9877      */
9878     markInvalid : function(errors){
9879         if(errors instanceof Array){
9880             for(var i = 0, len = errors.length; i < len; i++){
9881                 var fieldError = errors[i];
9882                 var f = this.findField(fieldError.id);
9883                 if(f){
9884                     f.markInvalid(fieldError.msg);
9885                 }
9886             }
9887         }else{
9888             var field, id;
9889             for(id in errors){
9890                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9891                     field.markInvalid(errors[id]);
9892                 }
9893             }
9894         }
9895         //Roo.each(this.childForms || [], function (f) {
9896         //    f.markInvalid(errors);
9897         //});
9898
9899         return this;
9900     },
9901
9902     /**
9903      * Set values for fields in this form in bulk.
9904      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9905      * @return {BasicForm} this
9906      */
9907     setValues : function(values){
9908         if(values instanceof Array){ // array of objects
9909             for(var i = 0, len = values.length; i < len; i++){
9910                 var v = values[i];
9911                 var f = this.findField(v.id);
9912                 if(f){
9913                     f.setValue(v.value);
9914                     if(this.trackResetOnLoad){
9915                         f.originalValue = f.getValue();
9916                     }
9917                 }
9918             }
9919         }else{ // object hash
9920             var field, id;
9921             for(id in values){
9922                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9923
9924                     if (field.setFromData &&
9925                         field.valueField &&
9926                         field.displayField &&
9927                         // combos' with local stores can
9928                         // be queried via setValue()
9929                         // to set their value..
9930                         (field.store && !field.store.isLocal)
9931                         ) {
9932                         // it's a combo
9933                         var sd = { };
9934                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9935                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9936                         field.setFromData(sd);
9937
9938                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9939                         
9940                         field.setFromData(values);
9941                         
9942                     } else {
9943                         field.setValue(values[id]);
9944                     }
9945
9946
9947                     if(this.trackResetOnLoad){
9948                         field.originalValue = field.getValue();
9949                     }
9950                 }
9951             }
9952         }
9953
9954         //Roo.each(this.childForms || [], function (f) {
9955         //    f.setValues(values);
9956         //});
9957
9958         return this;
9959     },
9960
9961     /**
9962      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9963      * they are returned as an array.
9964      * @param {Boolean} asString
9965      * @return {Object}
9966      */
9967     getValues : function(asString){
9968         //if (this.childForms) {
9969             // copy values from the child forms
9970         //    Roo.each(this.childForms, function (f) {
9971         //        this.setValues(f.getValues());
9972         //    }, this);
9973         //}
9974
9975
9976
9977         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9978         if(asString === true){
9979             return fs;
9980         }
9981         return Roo.urlDecode(fs);
9982     },
9983
9984     /**
9985      * Returns the fields in this form as an object with key/value pairs.
9986      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9987      * @return {Object}
9988      */
9989     getFieldValues : function(with_hidden)
9990     {
9991         var items = this.getItems();
9992         var ret = {};
9993         items.each(function(f){
9994             
9995             if (!f.getName()) {
9996                 return;
9997             }
9998             
9999             var v = f.getValue();
10000             
10001             if (f.inputType =='radio') {
10002                 if (typeof(ret[f.getName()]) == 'undefined') {
10003                     ret[f.getName()] = ''; // empty..
10004                 }
10005
10006                 if (!f.el.dom.checked) {
10007                     return;
10008
10009                 }
10010                 v = f.el.dom.value;
10011
10012             }
10013             
10014             if(f.xtype == 'MoneyField'){
10015                 ret[f.currencyName] = f.getCurrency();
10016             }
10017
10018             // not sure if this supported any more..
10019             if ((typeof(v) == 'object') && f.getRawValue) {
10020                 v = f.getRawValue() ; // dates..
10021             }
10022             // combo boxes where name != hiddenName...
10023             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10024                 ret[f.name] = f.getRawValue();
10025             }
10026             ret[f.getName()] = v;
10027         });
10028
10029         return ret;
10030     },
10031
10032     /**
10033      * Clears all invalid messages in this form.
10034      * @return {BasicForm} this
10035      */
10036     clearInvalid : function(){
10037         var items = this.getItems();
10038
10039         items.each(function(f){
10040            f.clearInvalid();
10041         });
10042
10043         return this;
10044     },
10045
10046     /**
10047      * Resets this form.
10048      * @return {BasicForm} this
10049      */
10050     reset : function(){
10051         var items = this.getItems();
10052         items.each(function(f){
10053             f.reset();
10054         });
10055
10056         Roo.each(this.childForms || [], function (f) {
10057             f.reset();
10058         });
10059
10060
10061         return this;
10062     },
10063     
10064     getItems : function()
10065     {
10066         var r=new Roo.util.MixedCollection(false, function(o){
10067             return o.id || (o.id = Roo.id());
10068         });
10069         var iter = function(el) {
10070             if (el.inputEl) {
10071                 r.add(el);
10072             }
10073             if (!el.items) {
10074                 return;
10075             }
10076             Roo.each(el.items,function(e) {
10077                 iter(e);
10078             });
10079         };
10080
10081         iter(this);
10082         return r;
10083     },
10084     
10085     hideFields : function(items)
10086     {
10087         Roo.each(items, function(i){
10088             
10089             var f = this.findField(i);
10090             
10091             if(!f){
10092                 return;
10093             }
10094             
10095             f.hide();
10096             
10097         }, this);
10098     },
10099     
10100     showFields : function(items)
10101     {
10102         Roo.each(items, function(i){
10103             
10104             var f = this.findField(i);
10105             
10106             if(!f){
10107                 return;
10108             }
10109             
10110             f.show();
10111             
10112         }, this);
10113     }
10114
10115 });
10116
10117 Roo.apply(Roo.bootstrap.Form, {
10118     
10119     popover : {
10120         
10121         padding : 5,
10122         
10123         isApplied : false,
10124         
10125         isMasked : false,
10126         
10127         form : false,
10128         
10129         target : false,
10130         
10131         toolTip : false,
10132         
10133         intervalID : false,
10134         
10135         maskEl : false,
10136         
10137         apply : function()
10138         {
10139             if(this.isApplied){
10140                 return;
10141             }
10142             
10143             this.maskEl = {
10144                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10145                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10146                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10147                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10148             };
10149             
10150             this.maskEl.top.enableDisplayMode("block");
10151             this.maskEl.left.enableDisplayMode("block");
10152             this.maskEl.bottom.enableDisplayMode("block");
10153             this.maskEl.right.enableDisplayMode("block");
10154             
10155             this.toolTip = new Roo.bootstrap.Tooltip({
10156                 cls : 'roo-form-error-popover',
10157                 alignment : {
10158                     'left' : ['r-l', [-2,0], 'right'],
10159                     'right' : ['l-r', [2,0], 'left'],
10160                     'bottom' : ['tl-bl', [0,2], 'top'],
10161                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10162                 }
10163             });
10164             
10165             this.toolTip.render(Roo.get(document.body));
10166
10167             this.toolTip.el.enableDisplayMode("block");
10168             
10169             Roo.get(document.body).on('click', function(){
10170                 this.unmask();
10171             }, this);
10172             
10173             Roo.get(document.body).on('touchstart', function(){
10174                 this.unmask();
10175             }, this);
10176             
10177             this.isApplied = true
10178         },
10179         
10180         mask : function(form, target)
10181         {
10182             this.form = form;
10183             
10184             this.target = target;
10185             
10186             if(!this.form.errorMask || !target.el){
10187                 return;
10188             }
10189             
10190             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10191             
10192             Roo.log(scrollable);
10193             
10194             var ot = this.target.el.calcOffsetsTo(scrollable);
10195             
10196             var scrollTo = ot[1] - this.form.maskOffset;
10197             
10198             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10199             
10200             scrollable.scrollTo('top', scrollTo);
10201             
10202             var box = this.target.el.getBox();
10203             Roo.log(box);
10204             var zIndex = Roo.bootstrap.Modal.zIndex++;
10205
10206             
10207             this.maskEl.top.setStyle('position', 'absolute');
10208             this.maskEl.top.setStyle('z-index', zIndex);
10209             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10210             this.maskEl.top.setLeft(0);
10211             this.maskEl.top.setTop(0);
10212             this.maskEl.top.show();
10213             
10214             this.maskEl.left.setStyle('position', 'absolute');
10215             this.maskEl.left.setStyle('z-index', zIndex);
10216             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10217             this.maskEl.left.setLeft(0);
10218             this.maskEl.left.setTop(box.y - this.padding);
10219             this.maskEl.left.show();
10220
10221             this.maskEl.bottom.setStyle('position', 'absolute');
10222             this.maskEl.bottom.setStyle('z-index', zIndex);
10223             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10224             this.maskEl.bottom.setLeft(0);
10225             this.maskEl.bottom.setTop(box.bottom + this.padding);
10226             this.maskEl.bottom.show();
10227
10228             this.maskEl.right.setStyle('position', 'absolute');
10229             this.maskEl.right.setStyle('z-index', zIndex);
10230             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10231             this.maskEl.right.setLeft(box.right + this.padding);
10232             this.maskEl.right.setTop(box.y - this.padding);
10233             this.maskEl.right.show();
10234
10235             this.toolTip.bindEl = this.target.el;
10236
10237             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10238
10239             var tip = this.target.blankText;
10240
10241             if(this.target.getValue() !== '' ) {
10242                 
10243                 if (this.target.invalidText.length) {
10244                     tip = this.target.invalidText;
10245                 } else if (this.target.regexText.length){
10246                     tip = this.target.regexText;
10247                 }
10248             }
10249
10250             this.toolTip.show(tip);
10251
10252             this.intervalID = window.setInterval(function() {
10253                 Roo.bootstrap.Form.popover.unmask();
10254             }, 10000);
10255
10256             window.onwheel = function(){ return false;};
10257             
10258             (function(){ this.isMasked = true; }).defer(500, this);
10259             
10260         },
10261         
10262         unmask : function()
10263         {
10264             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10265                 return;
10266             }
10267             
10268             this.maskEl.top.setStyle('position', 'absolute');
10269             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10270             this.maskEl.top.hide();
10271
10272             this.maskEl.left.setStyle('position', 'absolute');
10273             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10274             this.maskEl.left.hide();
10275
10276             this.maskEl.bottom.setStyle('position', 'absolute');
10277             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10278             this.maskEl.bottom.hide();
10279
10280             this.maskEl.right.setStyle('position', 'absolute');
10281             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10282             this.maskEl.right.hide();
10283             
10284             this.toolTip.hide();
10285             
10286             this.toolTip.el.hide();
10287             
10288             window.onwheel = function(){ return true;};
10289             
10290             if(this.intervalID){
10291                 window.clearInterval(this.intervalID);
10292                 this.intervalID = false;
10293             }
10294             
10295             this.isMasked = false;
10296             
10297         }
10298         
10299     }
10300     
10301 });
10302
10303 /*
10304  * Based on:
10305  * Ext JS Library 1.1.1
10306  * Copyright(c) 2006-2007, Ext JS, LLC.
10307  *
10308  * Originally Released Under LGPL - original licence link has changed is not relivant.
10309  *
10310  * Fork - LGPL
10311  * <script type="text/javascript">
10312  */
10313 /**
10314  * @class Roo.form.VTypes
10315  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10316  * @singleton
10317  */
10318 Roo.form.VTypes = function(){
10319     // closure these in so they are only created once.
10320     var alpha = /^[a-zA-Z_]+$/;
10321     var alphanum = /^[a-zA-Z0-9_]+$/;
10322     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10323     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10324
10325     // All these messages and functions are configurable
10326     return {
10327         /**
10328          * The function used to validate email addresses
10329          * @param {String} value The email address
10330          */
10331         'email' : function(v){
10332             return email.test(v);
10333         },
10334         /**
10335          * The error text to display when the email validation function returns false
10336          * @type String
10337          */
10338         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10339         /**
10340          * The keystroke filter mask to be applied on email input
10341          * @type RegExp
10342          */
10343         'emailMask' : /[a-z0-9_\.\-@]/i,
10344
10345         /**
10346          * The function used to validate URLs
10347          * @param {String} value The URL
10348          */
10349         'url' : function(v){
10350             return url.test(v);
10351         },
10352         /**
10353          * The error text to display when the url validation function returns false
10354          * @type String
10355          */
10356         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10357         
10358         /**
10359          * The function used to validate alpha values
10360          * @param {String} value The value
10361          */
10362         'alpha' : function(v){
10363             return alpha.test(v);
10364         },
10365         /**
10366          * The error text to display when the alpha validation function returns false
10367          * @type String
10368          */
10369         'alphaText' : 'This field should only contain letters and _',
10370         /**
10371          * The keystroke filter mask to be applied on alpha input
10372          * @type RegExp
10373          */
10374         'alphaMask' : /[a-z_]/i,
10375
10376         /**
10377          * The function used to validate alphanumeric values
10378          * @param {String} value The value
10379          */
10380         'alphanum' : function(v){
10381             return alphanum.test(v);
10382         },
10383         /**
10384          * The error text to display when the alphanumeric validation function returns false
10385          * @type String
10386          */
10387         'alphanumText' : 'This field should only contain letters, numbers and _',
10388         /**
10389          * The keystroke filter mask to be applied on alphanumeric input
10390          * @type RegExp
10391          */
10392         'alphanumMask' : /[a-z0-9_]/i
10393     };
10394 }();/*
10395  * - LGPL
10396  *
10397  * Input
10398  * 
10399  */
10400
10401 /**
10402  * @class Roo.bootstrap.Input
10403  * @extends Roo.bootstrap.Component
10404  * Bootstrap Input class
10405  * @cfg {Boolean} disabled is it disabled
10406  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10407  * @cfg {String} name name of the input
10408  * @cfg {string} fieldLabel - the label associated
10409  * @cfg {string} placeholder - placeholder to put in text.
10410  * @cfg {string}  before - input group add on before
10411  * @cfg {string} after - input group add on after
10412  * @cfg {string} size - (lg|sm) or leave empty..
10413  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10414  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10415  * @cfg {Number} md colspan out of 12 for computer-sized screens
10416  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10417  * @cfg {string} value default value of the input
10418  * @cfg {Number} labelWidth set the width of label 
10419  * @cfg {Number} labellg set the width of label (1-12)
10420  * @cfg {Number} labelmd set the width of label (1-12)
10421  * @cfg {Number} labelsm set the width of label (1-12)
10422  * @cfg {Number} labelxs set the width of label (1-12)
10423  * @cfg {String} labelAlign (top|left)
10424  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10425  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10426  * @cfg {String} indicatorpos (left|right) default left
10427  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10428  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10429  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10430
10431  * @cfg {String} align (left|center|right) Default left
10432  * @cfg {Boolean} forceFeedback (true|false) Default false
10433  * 
10434  * @constructor
10435  * Create a new Input
10436  * @param {Object} config The config object
10437  */
10438
10439 Roo.bootstrap.Input = function(config){
10440     
10441     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10442     
10443     this.addEvents({
10444         /**
10445          * @event focus
10446          * Fires when this field receives input focus.
10447          * @param {Roo.form.Field} this
10448          */
10449         focus : true,
10450         /**
10451          * @event blur
10452          * Fires when this field loses input focus.
10453          * @param {Roo.form.Field} this
10454          */
10455         blur : true,
10456         /**
10457          * @event specialkey
10458          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10459          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10460          * @param {Roo.form.Field} this
10461          * @param {Roo.EventObject} e The event object
10462          */
10463         specialkey : true,
10464         /**
10465          * @event change
10466          * Fires just before the field blurs if the field value has changed.
10467          * @param {Roo.form.Field} this
10468          * @param {Mixed} newValue The new value
10469          * @param {Mixed} oldValue The original value
10470          */
10471         change : true,
10472         /**
10473          * @event invalid
10474          * Fires after the field has been marked as invalid.
10475          * @param {Roo.form.Field} this
10476          * @param {String} msg The validation message
10477          */
10478         invalid : true,
10479         /**
10480          * @event valid
10481          * Fires after the field has been validated with no errors.
10482          * @param {Roo.form.Field} this
10483          */
10484         valid : true,
10485          /**
10486          * @event keyup
10487          * Fires after the key up
10488          * @param {Roo.form.Field} this
10489          * @param {Roo.EventObject}  e The event Object
10490          */
10491         keyup : true
10492     });
10493 };
10494
10495 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10496      /**
10497      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10498       automatic validation (defaults to "keyup").
10499      */
10500     validationEvent : "keyup",
10501      /**
10502      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10503      */
10504     validateOnBlur : true,
10505     /**
10506      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10507      */
10508     validationDelay : 250,
10509      /**
10510      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10511      */
10512     focusClass : "x-form-focus",  // not needed???
10513     
10514        
10515     /**
10516      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10517      */
10518     invalidClass : "has-warning",
10519     
10520     /**
10521      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10522      */
10523     validClass : "has-success",
10524     
10525     /**
10526      * @cfg {Boolean} hasFeedback (true|false) default true
10527      */
10528     hasFeedback : true,
10529     
10530     /**
10531      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10532      */
10533     invalidFeedbackClass : "glyphicon-warning-sign",
10534     
10535     /**
10536      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10537      */
10538     validFeedbackClass : "glyphicon-ok",
10539     
10540     /**
10541      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10542      */
10543     selectOnFocus : false,
10544     
10545      /**
10546      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10547      */
10548     maskRe : null,
10549        /**
10550      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10551      */
10552     vtype : null,
10553     
10554       /**
10555      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10556      */
10557     disableKeyFilter : false,
10558     
10559        /**
10560      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10561      */
10562     disabled : false,
10563      /**
10564      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10565      */
10566     allowBlank : true,
10567     /**
10568      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10569      */
10570     blankText : "Please complete this mandatory field",
10571     
10572      /**
10573      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10574      */
10575     minLength : 0,
10576     /**
10577      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10578      */
10579     maxLength : Number.MAX_VALUE,
10580     /**
10581      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10582      */
10583     minLengthText : "The minimum length for this field is {0}",
10584     /**
10585      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10586      */
10587     maxLengthText : "The maximum length for this field is {0}",
10588   
10589     
10590     /**
10591      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10592      * If available, this function will be called only after the basic validators all return true, and will be passed the
10593      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10594      */
10595     validator : null,
10596     /**
10597      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10598      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10599      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10600      */
10601     regex : null,
10602     /**
10603      * @cfg {String} regexText -- Depricated - use Invalid Text
10604      */
10605     regexText : "",
10606     
10607     /**
10608      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10609      */
10610     invalidText : "",
10611     
10612     
10613     
10614     autocomplete: false,
10615     
10616     
10617     fieldLabel : '',
10618     inputType : 'text',
10619     
10620     name : false,
10621     placeholder: false,
10622     before : false,
10623     after : false,
10624     size : false,
10625     hasFocus : false,
10626     preventMark: false,
10627     isFormField : true,
10628     value : '',
10629     labelWidth : 2,
10630     labelAlign : false,
10631     readOnly : false,
10632     align : false,
10633     formatedValue : false,
10634     forceFeedback : false,
10635     
10636     indicatorpos : 'left',
10637     
10638     labellg : 0,
10639     labelmd : 0,
10640     labelsm : 0,
10641     labelxs : 0,
10642     
10643     capture : '',
10644     accept : '',
10645     
10646     parentLabelAlign : function()
10647     {
10648         var parent = this;
10649         while (parent.parent()) {
10650             parent = parent.parent();
10651             if (typeof(parent.labelAlign) !='undefined') {
10652                 return parent.labelAlign;
10653             }
10654         }
10655         return 'left';
10656         
10657     },
10658     
10659     getAutoCreate : function()
10660     {
10661         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10662         
10663         var id = Roo.id();
10664         
10665         var cfg = {};
10666         
10667         if(this.inputType != 'hidden'){
10668             cfg.cls = 'form-group' //input-group
10669         }
10670         
10671         var input =  {
10672             tag: 'input',
10673             id : id,
10674             type : this.inputType,
10675             value : this.value,
10676             cls : 'form-control',
10677             placeholder : this.placeholder || '',
10678             autocomplete : this.autocomplete || 'new-password'
10679         };
10680         if (this.inputType == 'file') {
10681             input.style = 'overflow:hidden'; // why not in CSS?
10682         }
10683         
10684         if(this.capture.length){
10685             input.capture = this.capture;
10686         }
10687         
10688         if(this.accept.length){
10689             input.accept = this.accept + "/*";
10690         }
10691         
10692         if(this.align){
10693             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10694         }
10695         
10696         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10697             input.maxLength = this.maxLength;
10698         }
10699         
10700         if (this.disabled) {
10701             input.disabled=true;
10702         }
10703         
10704         if (this.readOnly) {
10705             input.readonly=true;
10706         }
10707         
10708         if (this.name) {
10709             input.name = this.name;
10710         }
10711         
10712         if (this.size) {
10713             input.cls += ' input-' + this.size;
10714         }
10715         
10716         var settings=this;
10717         ['xs','sm','md','lg'].map(function(size){
10718             if (settings[size]) {
10719                 cfg.cls += ' col-' + size + '-' + settings[size];
10720             }
10721         });
10722         
10723         var inputblock = input;
10724         
10725         var feedback = {
10726             tag: 'span',
10727             cls: 'glyphicon form-control-feedback'
10728         };
10729             
10730         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10731             
10732             inputblock = {
10733                 cls : 'has-feedback',
10734                 cn :  [
10735                     input,
10736                     feedback
10737                 ] 
10738             };  
10739         }
10740         
10741         if (this.before || this.after) {
10742             
10743             inputblock = {
10744                 cls : 'input-group',
10745                 cn :  [] 
10746             };
10747             
10748             if (this.before && typeof(this.before) == 'string') {
10749                 
10750                 inputblock.cn.push({
10751                     tag :'span',
10752                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10753                     html : this.before
10754                 });
10755             }
10756             if (this.before && typeof(this.before) == 'object') {
10757                 this.before = Roo.factory(this.before);
10758                 
10759                 inputblock.cn.push({
10760                     tag :'span',
10761                     cls : 'roo-input-before input-group-prepend   input-group-' +
10762                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10763                 });
10764             }
10765             
10766             inputblock.cn.push(input);
10767             
10768             if (this.after && typeof(this.after) == 'string') {
10769                 inputblock.cn.push({
10770                     tag :'span',
10771                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10772                     html : this.after
10773                 });
10774             }
10775             if (this.after && typeof(this.after) == 'object') {
10776                 this.after = Roo.factory(this.after);
10777                 
10778                 inputblock.cn.push({
10779                     tag :'span',
10780                     cls : 'roo-input-after input-group-append  input-group-' +
10781                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10782                 });
10783             }
10784             
10785             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10786                 inputblock.cls += ' has-feedback';
10787                 inputblock.cn.push(feedback);
10788             }
10789         };
10790         var indicator = {
10791             tag : 'i',
10792             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10793             tooltip : 'This field is required'
10794         };
10795         if (this.allowBlank ) {
10796             indicator.style = this.allowBlank ? ' display:none' : '';
10797         }
10798         if (align ==='left' && this.fieldLabel.length) {
10799             
10800             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10801             
10802             cfg.cn = [
10803                 indicator,
10804                 {
10805                     tag: 'label',
10806                     'for' :  id,
10807                     cls : 'control-label col-form-label',
10808                     html : this.fieldLabel
10809
10810                 },
10811                 {
10812                     cls : "", 
10813                     cn: [
10814                         inputblock
10815                     ]
10816                 }
10817             ];
10818             
10819             var labelCfg = cfg.cn[1];
10820             var contentCfg = cfg.cn[2];
10821             
10822             if(this.indicatorpos == 'right'){
10823                 cfg.cn = [
10824                     {
10825                         tag: 'label',
10826                         'for' :  id,
10827                         cls : 'control-label col-form-label',
10828                         cn : [
10829                             {
10830                                 tag : 'span',
10831                                 html : this.fieldLabel
10832                             },
10833                             indicator
10834                         ]
10835                     },
10836                     {
10837                         cls : "",
10838                         cn: [
10839                             inputblock
10840                         ]
10841                     }
10842
10843                 ];
10844                 
10845                 labelCfg = cfg.cn[0];
10846                 contentCfg = cfg.cn[1];
10847             
10848             }
10849             
10850             if(this.labelWidth > 12){
10851                 labelCfg.style = "width: " + this.labelWidth + 'px';
10852             }
10853             
10854             if(this.labelWidth < 13 && this.labelmd == 0){
10855                 this.labelmd = this.labelWidth;
10856             }
10857             
10858             if(this.labellg > 0){
10859                 labelCfg.cls += ' col-lg-' + this.labellg;
10860                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10861             }
10862             
10863             if(this.labelmd > 0){
10864                 labelCfg.cls += ' col-md-' + this.labelmd;
10865                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10866             }
10867             
10868             if(this.labelsm > 0){
10869                 labelCfg.cls += ' col-sm-' + this.labelsm;
10870                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10871             }
10872             
10873             if(this.labelxs > 0){
10874                 labelCfg.cls += ' col-xs-' + this.labelxs;
10875                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10876             }
10877             
10878             
10879         } else if ( this.fieldLabel.length) {
10880                 
10881             
10882             
10883             cfg.cn = [
10884                 {
10885                     tag : 'i',
10886                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10887                     tooltip : 'This field is required',
10888                     style : this.allowBlank ? ' display:none' : '' 
10889                 },
10890                 {
10891                     tag: 'label',
10892                    //cls : 'input-group-addon',
10893                     html : this.fieldLabel
10894
10895                 },
10896
10897                inputblock
10898
10899            ];
10900            
10901            if(this.indicatorpos == 'right'){
10902        
10903                 cfg.cn = [
10904                     {
10905                         tag: 'label',
10906                        //cls : 'input-group-addon',
10907                         html : this.fieldLabel
10908
10909                     },
10910                     {
10911                         tag : 'i',
10912                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10913                         tooltip : 'This field is required',
10914                         style : this.allowBlank ? ' display:none' : '' 
10915                     },
10916
10917                    inputblock
10918
10919                ];
10920
10921             }
10922
10923         } else {
10924             
10925             cfg.cn = [
10926
10927                     inputblock
10928
10929             ];
10930                 
10931                 
10932         };
10933         
10934         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10935            cfg.cls += ' navbar-form';
10936         }
10937         
10938         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10939             // on BS4 we do this only if not form 
10940             cfg.cls += ' navbar-form';
10941             cfg.tag = 'li';
10942         }
10943         
10944         return cfg;
10945         
10946     },
10947     /**
10948      * return the real input element.
10949      */
10950     inputEl: function ()
10951     {
10952         return this.el.select('input.form-control',true).first();
10953     },
10954     
10955     tooltipEl : function()
10956     {
10957         return this.inputEl();
10958     },
10959     
10960     indicatorEl : function()
10961     {
10962         if (Roo.bootstrap.version == 4) {
10963             return false; // not enabled in v4 yet.
10964         }
10965         
10966         var indicator = this.el.select('i.roo-required-indicator',true).first();
10967         
10968         if(!indicator){
10969             return false;
10970         }
10971         
10972         return indicator;
10973         
10974     },
10975     
10976     setDisabled : function(v)
10977     {
10978         var i  = this.inputEl().dom;
10979         if (!v) {
10980             i.removeAttribute('disabled');
10981             return;
10982             
10983         }
10984         i.setAttribute('disabled','true');
10985     },
10986     initEvents : function()
10987     {
10988           
10989         this.inputEl().on("keydown" , this.fireKey,  this);
10990         this.inputEl().on("focus", this.onFocus,  this);
10991         this.inputEl().on("blur", this.onBlur,  this);
10992         
10993         this.inputEl().relayEvent('keyup', this);
10994         
10995         this.indicator = this.indicatorEl();
10996         
10997         if(this.indicator){
10998             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10999         }
11000  
11001         // reference to original value for reset
11002         this.originalValue = this.getValue();
11003         //Roo.form.TextField.superclass.initEvents.call(this);
11004         if(this.validationEvent == 'keyup'){
11005             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11006             this.inputEl().on('keyup', this.filterValidation, this);
11007         }
11008         else if(this.validationEvent !== false){
11009             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11010         }
11011         
11012         if(this.selectOnFocus){
11013             this.on("focus", this.preFocus, this);
11014             
11015         }
11016         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11017             this.inputEl().on("keypress", this.filterKeys, this);
11018         } else {
11019             this.inputEl().relayEvent('keypress', this);
11020         }
11021        /* if(this.grow){
11022             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11023             this.el.on("click", this.autoSize,  this);
11024         }
11025         */
11026         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11027             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11028         }
11029         
11030         if (typeof(this.before) == 'object') {
11031             this.before.render(this.el.select('.roo-input-before',true).first());
11032         }
11033         if (typeof(this.after) == 'object') {
11034             this.after.render(this.el.select('.roo-input-after',true).first());
11035         }
11036         
11037         this.inputEl().on('change', this.onChange, this);
11038         
11039     },
11040     filterValidation : function(e){
11041         if(!e.isNavKeyPress()){
11042             this.validationTask.delay(this.validationDelay);
11043         }
11044     },
11045      /**
11046      * Validates the field value
11047      * @return {Boolean} True if the value is valid, else false
11048      */
11049     validate : function(){
11050         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11051         if(this.disabled || this.validateValue(this.getRawValue())){
11052             this.markValid();
11053             return true;
11054         }
11055         
11056         this.markInvalid();
11057         return false;
11058     },
11059     
11060     
11061     /**
11062      * Validates a value according to the field's validation rules and marks the field as invalid
11063      * if the validation fails
11064      * @param {Mixed} value The value to validate
11065      * @return {Boolean} True if the value is valid, else false
11066      */
11067     validateValue : function(value)
11068     {
11069         if(this.getVisibilityEl().hasClass('hidden')){
11070             return true;
11071         }
11072         
11073         if(value.length < 1)  { // if it's blank
11074             if(this.allowBlank){
11075                 return true;
11076             }
11077             return false;
11078         }
11079         
11080         if(value.length < this.minLength){
11081             return false;
11082         }
11083         if(value.length > this.maxLength){
11084             return false;
11085         }
11086         if(this.vtype){
11087             var vt = Roo.form.VTypes;
11088             if(!vt[this.vtype](value, this)){
11089                 return false;
11090             }
11091         }
11092         if(typeof this.validator == "function"){
11093             var msg = this.validator(value);
11094             if(msg !== true){
11095                 return false;
11096             }
11097             if (typeof(msg) == 'string') {
11098                 this.invalidText = msg;
11099             }
11100         }
11101         
11102         if(this.regex && !this.regex.test(value)){
11103             return false;
11104         }
11105         
11106         return true;
11107     },
11108     
11109      // private
11110     fireKey : function(e){
11111         //Roo.log('field ' + e.getKey());
11112         if(e.isNavKeyPress()){
11113             this.fireEvent("specialkey", this, e);
11114         }
11115     },
11116     focus : function (selectText){
11117         if(this.rendered){
11118             this.inputEl().focus();
11119             if(selectText === true){
11120                 this.inputEl().dom.select();
11121             }
11122         }
11123         return this;
11124     } ,
11125     
11126     onFocus : function(){
11127         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11128            // this.el.addClass(this.focusClass);
11129         }
11130         if(!this.hasFocus){
11131             this.hasFocus = true;
11132             this.startValue = this.getValue();
11133             this.fireEvent("focus", this);
11134         }
11135     },
11136     
11137     beforeBlur : Roo.emptyFn,
11138
11139     
11140     // private
11141     onBlur : function(){
11142         this.beforeBlur();
11143         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11144             //this.el.removeClass(this.focusClass);
11145         }
11146         this.hasFocus = false;
11147         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11148             this.validate();
11149         }
11150         var v = this.getValue();
11151         if(String(v) !== String(this.startValue)){
11152             this.fireEvent('change', this, v, this.startValue);
11153         }
11154         this.fireEvent("blur", this);
11155     },
11156     
11157     onChange : function(e)
11158     {
11159         var v = this.getValue();
11160         if(String(v) !== String(this.startValue)){
11161             this.fireEvent('change', this, v, this.startValue);
11162         }
11163         
11164     },
11165     
11166     /**
11167      * Resets the current field value to the originally loaded value and clears any validation messages
11168      */
11169     reset : function(){
11170         this.setValue(this.originalValue);
11171         this.validate();
11172     },
11173      /**
11174      * Returns the name of the field
11175      * @return {Mixed} name The name field
11176      */
11177     getName: function(){
11178         return this.name;
11179     },
11180      /**
11181      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11182      * @return {Mixed} value The field value
11183      */
11184     getValue : function(){
11185         
11186         var v = this.inputEl().getValue();
11187         
11188         return v;
11189     },
11190     /**
11191      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11192      * @return {Mixed} value The field value
11193      */
11194     getRawValue : function(){
11195         var v = this.inputEl().getValue();
11196         
11197         return v;
11198     },
11199     
11200     /**
11201      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11202      * @param {Mixed} value The value to set
11203      */
11204     setRawValue : function(v){
11205         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11206     },
11207     
11208     selectText : function(start, end){
11209         var v = this.getRawValue();
11210         if(v.length > 0){
11211             start = start === undefined ? 0 : start;
11212             end = end === undefined ? v.length : end;
11213             var d = this.inputEl().dom;
11214             if(d.setSelectionRange){
11215                 d.setSelectionRange(start, end);
11216             }else if(d.createTextRange){
11217                 var range = d.createTextRange();
11218                 range.moveStart("character", start);
11219                 range.moveEnd("character", v.length-end);
11220                 range.select();
11221             }
11222         }
11223     },
11224     
11225     /**
11226      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11227      * @param {Mixed} value The value to set
11228      */
11229     setValue : function(v){
11230         this.value = v;
11231         if(this.rendered){
11232             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11233             this.validate();
11234         }
11235     },
11236     
11237     /*
11238     processValue : function(value){
11239         if(this.stripCharsRe){
11240             var newValue = value.replace(this.stripCharsRe, '');
11241             if(newValue !== value){
11242                 this.setRawValue(newValue);
11243                 return newValue;
11244             }
11245         }
11246         return value;
11247     },
11248   */
11249     preFocus : function(){
11250         
11251         if(this.selectOnFocus){
11252             this.inputEl().dom.select();
11253         }
11254     },
11255     filterKeys : function(e){
11256         var k = e.getKey();
11257         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11258             return;
11259         }
11260         var c = e.getCharCode(), cc = String.fromCharCode(c);
11261         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11262             return;
11263         }
11264         if(!this.maskRe.test(cc)){
11265             e.stopEvent();
11266         }
11267     },
11268      /**
11269      * Clear any invalid styles/messages for this field
11270      */
11271     clearInvalid : function(){
11272         
11273         if(!this.el || this.preventMark){ // not rendered
11274             return;
11275         }
11276         
11277         
11278         this.el.removeClass([this.invalidClass, 'is-invalid']);
11279         
11280         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11281             
11282             var feedback = this.el.select('.form-control-feedback', true).first();
11283             
11284             if(feedback){
11285                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11286             }
11287             
11288         }
11289         
11290         if(this.indicator){
11291             this.indicator.removeClass('visible');
11292             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11293         }
11294         
11295         this.fireEvent('valid', this);
11296     },
11297     
11298      /**
11299      * Mark this field as valid
11300      */
11301     markValid : function()
11302     {
11303         if(!this.el  || this.preventMark){ // not rendered...
11304             return;
11305         }
11306         
11307         this.el.removeClass([this.invalidClass, this.validClass]);
11308         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11309
11310         var feedback = this.el.select('.form-control-feedback', true).first();
11311             
11312         if(feedback){
11313             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11314         }
11315         
11316         if(this.indicator){
11317             this.indicator.removeClass('visible');
11318             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11319         }
11320         
11321         if(this.disabled){
11322             return;
11323         }
11324         
11325            
11326         if(this.allowBlank && !this.getRawValue().length){
11327             return;
11328         }
11329         if (Roo.bootstrap.version == 3) {
11330             this.el.addClass(this.validClass);
11331         } else {
11332             this.inputEl().addClass('is-valid');
11333         }
11334
11335         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11336             
11337             var feedback = this.el.select('.form-control-feedback', true).first();
11338             
11339             if(feedback){
11340                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11341                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11342             }
11343             
11344         }
11345         
11346         this.fireEvent('valid', this);
11347     },
11348     
11349      /**
11350      * Mark this field as invalid
11351      * @param {String} msg The validation message
11352      */
11353     markInvalid : function(msg)
11354     {
11355         if(!this.el  || this.preventMark){ // not rendered
11356             return;
11357         }
11358         
11359         this.el.removeClass([this.invalidClass, this.validClass]);
11360         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11361         
11362         var feedback = this.el.select('.form-control-feedback', true).first();
11363             
11364         if(feedback){
11365             this.el.select('.form-control-feedback', true).first().removeClass(
11366                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11367         }
11368
11369         if(this.disabled){
11370             return;
11371         }
11372         
11373         if(this.allowBlank && !this.getRawValue().length){
11374             return;
11375         }
11376         
11377         if(this.indicator){
11378             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11379             this.indicator.addClass('visible');
11380         }
11381         if (Roo.bootstrap.version == 3) {
11382             this.el.addClass(this.invalidClass);
11383         } else {
11384             this.inputEl().addClass('is-invalid');
11385         }
11386         
11387         
11388         
11389         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11390             
11391             var feedback = this.el.select('.form-control-feedback', true).first();
11392             
11393             if(feedback){
11394                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11395                 
11396                 if(this.getValue().length || this.forceFeedback){
11397                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11398                 }
11399                 
11400             }
11401             
11402         }
11403         
11404         this.fireEvent('invalid', this, msg);
11405     },
11406     // private
11407     SafariOnKeyDown : function(event)
11408     {
11409         // this is a workaround for a password hang bug on chrome/ webkit.
11410         if (this.inputEl().dom.type != 'password') {
11411             return;
11412         }
11413         
11414         var isSelectAll = false;
11415         
11416         if(this.inputEl().dom.selectionEnd > 0){
11417             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11418         }
11419         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11420             event.preventDefault();
11421             this.setValue('');
11422             return;
11423         }
11424         
11425         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11426             
11427             event.preventDefault();
11428             // this is very hacky as keydown always get's upper case.
11429             //
11430             var cc = String.fromCharCode(event.getCharCode());
11431             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11432             
11433         }
11434     },
11435     adjustWidth : function(tag, w){
11436         tag = tag.toLowerCase();
11437         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11438             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11439                 if(tag == 'input'){
11440                     return w + 2;
11441                 }
11442                 if(tag == 'textarea'){
11443                     return w-2;
11444                 }
11445             }else if(Roo.isOpera){
11446                 if(tag == 'input'){
11447                     return w + 2;
11448                 }
11449                 if(tag == 'textarea'){
11450                     return w-2;
11451                 }
11452             }
11453         }
11454         return w;
11455     },
11456     
11457     setFieldLabel : function(v)
11458     {
11459         if(!this.rendered){
11460             return;
11461         }
11462         
11463         if(this.indicatorEl()){
11464             var ar = this.el.select('label > span',true);
11465             
11466             if (ar.elements.length) {
11467                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11468                 this.fieldLabel = v;
11469                 return;
11470             }
11471             
11472             var br = this.el.select('label',true);
11473             
11474             if(br.elements.length) {
11475                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11476                 this.fieldLabel = v;
11477                 return;
11478             }
11479             
11480             Roo.log('Cannot Found any of label > span || label in input');
11481             return;
11482         }
11483         
11484         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11485         this.fieldLabel = v;
11486         
11487         
11488     }
11489 });
11490
11491  
11492 /*
11493  * - LGPL
11494  *
11495  * Input
11496  * 
11497  */
11498
11499 /**
11500  * @class Roo.bootstrap.TextArea
11501  * @extends Roo.bootstrap.Input
11502  * Bootstrap TextArea class
11503  * @cfg {Number} cols Specifies the visible width of a text area
11504  * @cfg {Number} rows Specifies the visible number of lines in a text area
11505  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11506  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11507  * @cfg {string} html text
11508  * 
11509  * @constructor
11510  * Create a new TextArea
11511  * @param {Object} config The config object
11512  */
11513
11514 Roo.bootstrap.TextArea = function(config){
11515     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11516    
11517 };
11518
11519 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11520      
11521     cols : false,
11522     rows : 5,
11523     readOnly : false,
11524     warp : 'soft',
11525     resize : false,
11526     value: false,
11527     html: false,
11528     
11529     getAutoCreate : function(){
11530         
11531         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11532         
11533         var id = Roo.id();
11534         
11535         var cfg = {};
11536         
11537         if(this.inputType != 'hidden'){
11538             cfg.cls = 'form-group' //input-group
11539         }
11540         
11541         var input =  {
11542             tag: 'textarea',
11543             id : id,
11544             warp : this.warp,
11545             rows : this.rows,
11546             value : this.value || '',
11547             html: this.html || '',
11548             cls : 'form-control',
11549             placeholder : this.placeholder || '' 
11550             
11551         };
11552         
11553         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11554             input.maxLength = this.maxLength;
11555         }
11556         
11557         if(this.resize){
11558             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11559         }
11560         
11561         if(this.cols){
11562             input.cols = this.cols;
11563         }
11564         
11565         if (this.readOnly) {
11566             input.readonly = true;
11567         }
11568         
11569         if (this.name) {
11570             input.name = this.name;
11571         }
11572         
11573         if (this.size) {
11574             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11575         }
11576         
11577         var settings=this;
11578         ['xs','sm','md','lg'].map(function(size){
11579             if (settings[size]) {
11580                 cfg.cls += ' col-' + size + '-' + settings[size];
11581             }
11582         });
11583         
11584         var inputblock = input;
11585         
11586         if(this.hasFeedback && !this.allowBlank){
11587             
11588             var feedback = {
11589                 tag: 'span',
11590                 cls: 'glyphicon form-control-feedback'
11591             };
11592
11593             inputblock = {
11594                 cls : 'has-feedback',
11595                 cn :  [
11596                     input,
11597                     feedback
11598                 ] 
11599             };  
11600         }
11601         
11602         
11603         if (this.before || this.after) {
11604             
11605             inputblock = {
11606                 cls : 'input-group',
11607                 cn :  [] 
11608             };
11609             if (this.before) {
11610                 inputblock.cn.push({
11611                     tag :'span',
11612                     cls : 'input-group-addon',
11613                     html : this.before
11614                 });
11615             }
11616             
11617             inputblock.cn.push(input);
11618             
11619             if(this.hasFeedback && !this.allowBlank){
11620                 inputblock.cls += ' has-feedback';
11621                 inputblock.cn.push(feedback);
11622             }
11623             
11624             if (this.after) {
11625                 inputblock.cn.push({
11626                     tag :'span',
11627                     cls : 'input-group-addon',
11628                     html : this.after
11629                 });
11630             }
11631             
11632         }
11633         
11634         if (align ==='left' && this.fieldLabel.length) {
11635             cfg.cn = [
11636                 {
11637                     tag: 'label',
11638                     'for' :  id,
11639                     cls : 'control-label',
11640                     html : this.fieldLabel
11641                 },
11642                 {
11643                     cls : "",
11644                     cn: [
11645                         inputblock
11646                     ]
11647                 }
11648
11649             ];
11650             
11651             if(this.labelWidth > 12){
11652                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11653             }
11654
11655             if(this.labelWidth < 13 && this.labelmd == 0){
11656                 this.labelmd = this.labelWidth;
11657             }
11658
11659             if(this.labellg > 0){
11660                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11661                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11662             }
11663
11664             if(this.labelmd > 0){
11665                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11666                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11667             }
11668
11669             if(this.labelsm > 0){
11670                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11671                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11672             }
11673
11674             if(this.labelxs > 0){
11675                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11676                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11677             }
11678             
11679         } else if ( this.fieldLabel.length) {
11680             cfg.cn = [
11681
11682                {
11683                    tag: 'label',
11684                    //cls : 'input-group-addon',
11685                    html : this.fieldLabel
11686
11687                },
11688
11689                inputblock
11690
11691            ];
11692
11693         } else {
11694
11695             cfg.cn = [
11696
11697                 inputblock
11698
11699             ];
11700                 
11701         }
11702         
11703         if (this.disabled) {
11704             input.disabled=true;
11705         }
11706         
11707         return cfg;
11708         
11709     },
11710     /**
11711      * return the real textarea element.
11712      */
11713     inputEl: function ()
11714     {
11715         return this.el.select('textarea.form-control',true).first();
11716     },
11717     
11718     /**
11719      * Clear any invalid styles/messages for this field
11720      */
11721     clearInvalid : function()
11722     {
11723         
11724         if(!this.el || this.preventMark){ // not rendered
11725             return;
11726         }
11727         
11728         var label = this.el.select('label', true).first();
11729         var icon = this.el.select('i.fa-star', true).first();
11730         
11731         if(label && icon){
11732             icon.remove();
11733         }
11734         this.el.removeClass( this.validClass);
11735         this.inputEl().removeClass('is-invalid');
11736          
11737         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11738             
11739             var feedback = this.el.select('.form-control-feedback', true).first();
11740             
11741             if(feedback){
11742                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11743             }
11744             
11745         }
11746         
11747         this.fireEvent('valid', this);
11748     },
11749     
11750      /**
11751      * Mark this field as valid
11752      */
11753     markValid : function()
11754     {
11755         if(!this.el  || this.preventMark){ // not rendered
11756             return;
11757         }
11758         
11759         this.el.removeClass([this.invalidClass, this.validClass]);
11760         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11761         
11762         var feedback = this.el.select('.form-control-feedback', true).first();
11763             
11764         if(feedback){
11765             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11766         }
11767
11768         if(this.disabled || this.allowBlank){
11769             return;
11770         }
11771         
11772         var label = this.el.select('label', true).first();
11773         var icon = this.el.select('i.fa-star', true).first();
11774         
11775         if(label && icon){
11776             icon.remove();
11777         }
11778         if (Roo.bootstrap.version == 3) {
11779             this.el.addClass(this.validClass);
11780         } else {
11781             this.inputEl().addClass('is-valid');
11782         }
11783         
11784         
11785         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11786             
11787             var feedback = this.el.select('.form-control-feedback', true).first();
11788             
11789             if(feedback){
11790                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11791                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11792             }
11793             
11794         }
11795         
11796         this.fireEvent('valid', this);
11797     },
11798     
11799      /**
11800      * Mark this field as invalid
11801      * @param {String} msg The validation message
11802      */
11803     markInvalid : function(msg)
11804     {
11805         if(!this.el  || this.preventMark){ // not rendered
11806             return;
11807         }
11808         
11809         this.el.removeClass([this.invalidClass, this.validClass]);
11810         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11811         
11812         var feedback = this.el.select('.form-control-feedback', true).first();
11813             
11814         if(feedback){
11815             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11816         }
11817
11818         if(this.disabled || this.allowBlank){
11819             return;
11820         }
11821         
11822         var label = this.el.select('label', true).first();
11823         var icon = this.el.select('i.fa-star', true).first();
11824         
11825         if(!this.getValue().length && label && !icon){
11826             this.el.createChild({
11827                 tag : 'i',
11828                 cls : 'text-danger fa fa-lg fa-star',
11829                 tooltip : 'This field is required',
11830                 style : 'margin-right:5px;'
11831             }, label, true);
11832         }
11833         
11834         if (Roo.bootstrap.version == 3) {
11835             this.el.addClass(this.invalidClass);
11836         } else {
11837             this.inputEl().addClass('is-invalid');
11838         }
11839         
11840         // fixme ... this may be depricated need to test..
11841         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11842             
11843             var feedback = this.el.select('.form-control-feedback', true).first();
11844             
11845             if(feedback){
11846                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11847                 
11848                 if(this.getValue().length || this.forceFeedback){
11849                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11850                 }
11851                 
11852             }
11853             
11854         }
11855         
11856         this.fireEvent('invalid', this, msg);
11857     }
11858 });
11859
11860  
11861 /*
11862  * - LGPL
11863  *
11864  * trigger field - base class for combo..
11865  * 
11866  */
11867  
11868 /**
11869  * @class Roo.bootstrap.TriggerField
11870  * @extends Roo.bootstrap.Input
11871  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11872  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11873  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11874  * for which you can provide a custom implementation.  For example:
11875  * <pre><code>
11876 var trigger = new Roo.bootstrap.TriggerField();
11877 trigger.onTriggerClick = myTriggerFn;
11878 trigger.applyTo('my-field');
11879 </code></pre>
11880  *
11881  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11882  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11883  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11884  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11885  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11886
11887  * @constructor
11888  * Create a new TriggerField.
11889  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11890  * to the base TextField)
11891  */
11892 Roo.bootstrap.TriggerField = function(config){
11893     this.mimicing = false;
11894     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11895 };
11896
11897 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11898     /**
11899      * @cfg {String} triggerClass A CSS class to apply to the trigger
11900      */
11901      /**
11902      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11903      */
11904     hideTrigger:false,
11905
11906     /**
11907      * @cfg {Boolean} removable (true|false) special filter default false
11908      */
11909     removable : false,
11910     
11911     /** @cfg {Boolean} grow @hide */
11912     /** @cfg {Number} growMin @hide */
11913     /** @cfg {Number} growMax @hide */
11914
11915     /**
11916      * @hide 
11917      * @method
11918      */
11919     autoSize: Roo.emptyFn,
11920     // private
11921     monitorTab : true,
11922     // private
11923     deferHeight : true,
11924
11925     
11926     actionMode : 'wrap',
11927     
11928     caret : false,
11929     
11930     
11931     getAutoCreate : function(){
11932        
11933         var align = this.labelAlign || this.parentLabelAlign();
11934         
11935         var id = Roo.id();
11936         
11937         var cfg = {
11938             cls: 'form-group' //input-group
11939         };
11940         
11941         
11942         var input =  {
11943             tag: 'input',
11944             id : id,
11945             type : this.inputType,
11946             cls : 'form-control',
11947             autocomplete: 'new-password',
11948             placeholder : this.placeholder || '' 
11949             
11950         };
11951         if (this.name) {
11952             input.name = this.name;
11953         }
11954         if (this.size) {
11955             input.cls += ' input-' + this.size;
11956         }
11957         
11958         if (this.disabled) {
11959             input.disabled=true;
11960         }
11961         
11962         var inputblock = input;
11963         
11964         if(this.hasFeedback && !this.allowBlank){
11965             
11966             var feedback = {
11967                 tag: 'span',
11968                 cls: 'glyphicon form-control-feedback'
11969             };
11970             
11971             if(this.removable && !this.editable  ){
11972                 inputblock = {
11973                     cls : 'has-feedback',
11974                     cn :  [
11975                         inputblock,
11976                         {
11977                             tag: 'button',
11978                             html : 'x',
11979                             cls : 'roo-combo-removable-btn close'
11980                         },
11981                         feedback
11982                     ] 
11983                 };
11984             } else {
11985                 inputblock = {
11986                     cls : 'has-feedback',
11987                     cn :  [
11988                         inputblock,
11989                         feedback
11990                     ] 
11991                 };
11992             }
11993
11994         } else {
11995             if(this.removable && !this.editable ){
11996                 inputblock = {
11997                     cls : 'roo-removable',
11998                     cn :  [
11999                         inputblock,
12000                         {
12001                             tag: 'button',
12002                             html : 'x',
12003                             cls : 'roo-combo-removable-btn close'
12004                         }
12005                     ] 
12006                 };
12007             }
12008         }
12009         
12010         if (this.before || this.after) {
12011             
12012             inputblock = {
12013                 cls : 'input-group',
12014                 cn :  [] 
12015             };
12016             if (this.before) {
12017                 inputblock.cn.push({
12018                     tag :'span',
12019                     cls : 'input-group-addon input-group-prepend input-group-text',
12020                     html : this.before
12021                 });
12022             }
12023             
12024             inputblock.cn.push(input);
12025             
12026             if(this.hasFeedback && !this.allowBlank){
12027                 inputblock.cls += ' has-feedback';
12028                 inputblock.cn.push(feedback);
12029             }
12030             
12031             if (this.after) {
12032                 inputblock.cn.push({
12033                     tag :'span',
12034                     cls : 'input-group-addon input-group-append input-group-text',
12035                     html : this.after
12036                 });
12037             }
12038             
12039         };
12040         
12041       
12042         
12043         var ibwrap = inputblock;
12044         
12045         if(this.multiple){
12046             ibwrap = {
12047                 tag: 'ul',
12048                 cls: 'roo-select2-choices',
12049                 cn:[
12050                     {
12051                         tag: 'li',
12052                         cls: 'roo-select2-search-field',
12053                         cn: [
12054
12055                             inputblock
12056                         ]
12057                     }
12058                 ]
12059             };
12060                 
12061         }
12062         
12063         var combobox = {
12064             cls: 'roo-select2-container input-group',
12065             cn: [
12066                  {
12067                     tag: 'input',
12068                     type : 'hidden',
12069                     cls: 'form-hidden-field'
12070                 },
12071                 ibwrap
12072             ]
12073         };
12074         
12075         if(!this.multiple && this.showToggleBtn){
12076             
12077             var caret = {
12078                         tag: 'span',
12079                         cls: 'caret'
12080              };
12081             if (this.caret != false) {
12082                 caret = {
12083                      tag: 'i',
12084                      cls: 'fa fa-' + this.caret
12085                 };
12086                 
12087             }
12088             
12089             combobox.cn.push({
12090                 tag :'span',
12091                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12092                 cn : [
12093                     Roo.bootstrap.version == 3 ? caret : '',
12094                     {
12095                         tag: 'span',
12096                         cls: 'combobox-clear',
12097                         cn  : [
12098                             {
12099                                 tag : 'i',
12100                                 cls: 'icon-remove'
12101                             }
12102                         ]
12103                     }
12104                 ]
12105
12106             })
12107         }
12108         
12109         if(this.multiple){
12110             combobox.cls += ' roo-select2-container-multi';
12111         }
12112          var indicator = {
12113             tag : 'i',
12114             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12115             tooltip : 'This field is required'
12116         };
12117         if (Roo.bootstrap.version == 4) {
12118             indicator = {
12119                 tag : 'i',
12120                 style : 'display:none'
12121             };
12122         }
12123         
12124         
12125         if (align ==='left' && this.fieldLabel.length) {
12126             
12127             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12128
12129             cfg.cn = [
12130                 indicator,
12131                 {
12132                     tag: 'label',
12133                     'for' :  id,
12134                     cls : 'control-label',
12135                     html : this.fieldLabel
12136
12137                 },
12138                 {
12139                     cls : "", 
12140                     cn: [
12141                         combobox
12142                     ]
12143                 }
12144
12145             ];
12146             
12147             var labelCfg = cfg.cn[1];
12148             var contentCfg = cfg.cn[2];
12149             
12150             if(this.indicatorpos == 'right'){
12151                 cfg.cn = [
12152                     {
12153                         tag: 'label',
12154                         'for' :  id,
12155                         cls : 'control-label',
12156                         cn : [
12157                             {
12158                                 tag : 'span',
12159                                 html : this.fieldLabel
12160                             },
12161                             indicator
12162                         ]
12163                     },
12164                     {
12165                         cls : "", 
12166                         cn: [
12167                             combobox
12168                         ]
12169                     }
12170
12171                 ];
12172                 
12173                 labelCfg = cfg.cn[0];
12174                 contentCfg = cfg.cn[1];
12175             }
12176             
12177             if(this.labelWidth > 12){
12178                 labelCfg.style = "width: " + this.labelWidth + 'px';
12179             }
12180             
12181             if(this.labelWidth < 13 && this.labelmd == 0){
12182                 this.labelmd = this.labelWidth;
12183             }
12184             
12185             if(this.labellg > 0){
12186                 labelCfg.cls += ' col-lg-' + this.labellg;
12187                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12188             }
12189             
12190             if(this.labelmd > 0){
12191                 labelCfg.cls += ' col-md-' + this.labelmd;
12192                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12193             }
12194             
12195             if(this.labelsm > 0){
12196                 labelCfg.cls += ' col-sm-' + this.labelsm;
12197                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12198             }
12199             
12200             if(this.labelxs > 0){
12201                 labelCfg.cls += ' col-xs-' + this.labelxs;
12202                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12203             }
12204             
12205         } else if ( this.fieldLabel.length) {
12206 //                Roo.log(" label");
12207             cfg.cn = [
12208                 indicator,
12209                {
12210                    tag: 'label',
12211                    //cls : 'input-group-addon',
12212                    html : this.fieldLabel
12213
12214                },
12215
12216                combobox
12217
12218             ];
12219             
12220             if(this.indicatorpos == 'right'){
12221                 
12222                 cfg.cn = [
12223                     {
12224                        tag: 'label',
12225                        cn : [
12226                            {
12227                                tag : 'span',
12228                                html : this.fieldLabel
12229                            },
12230                            indicator
12231                        ]
12232
12233                     },
12234                     combobox
12235
12236                 ];
12237
12238             }
12239
12240         } else {
12241             
12242 //                Roo.log(" no label && no align");
12243                 cfg = combobox
12244                      
12245                 
12246         }
12247         
12248         var settings=this;
12249         ['xs','sm','md','lg'].map(function(size){
12250             if (settings[size]) {
12251                 cfg.cls += ' col-' + size + '-' + settings[size];
12252             }
12253         });
12254         
12255         return cfg;
12256         
12257     },
12258     
12259     
12260     
12261     // private
12262     onResize : function(w, h){
12263 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12264 //        if(typeof w == 'number'){
12265 //            var x = w - this.trigger.getWidth();
12266 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12267 //            this.trigger.setStyle('left', x+'px');
12268 //        }
12269     },
12270
12271     // private
12272     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12273
12274     // private
12275     getResizeEl : function(){
12276         return this.inputEl();
12277     },
12278
12279     // private
12280     getPositionEl : function(){
12281         return this.inputEl();
12282     },
12283
12284     // private
12285     alignErrorIcon : function(){
12286         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12287     },
12288
12289     // private
12290     initEvents : function(){
12291         
12292         this.createList();
12293         
12294         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12295         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12296         if(!this.multiple && this.showToggleBtn){
12297             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12298             if(this.hideTrigger){
12299                 this.trigger.setDisplayed(false);
12300             }
12301             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12302         }
12303         
12304         if(this.multiple){
12305             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12306         }
12307         
12308         if(this.removable && !this.editable && !this.tickable){
12309             var close = this.closeTriggerEl();
12310             
12311             if(close){
12312                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12313                 close.on('click', this.removeBtnClick, this, close);
12314             }
12315         }
12316         
12317         //this.trigger.addClassOnOver('x-form-trigger-over');
12318         //this.trigger.addClassOnClick('x-form-trigger-click');
12319         
12320         //if(!this.width){
12321         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12322         //}
12323     },
12324     
12325     closeTriggerEl : function()
12326     {
12327         var close = this.el.select('.roo-combo-removable-btn', true).first();
12328         return close ? close : false;
12329     },
12330     
12331     removeBtnClick : function(e, h, el)
12332     {
12333         e.preventDefault();
12334         
12335         if(this.fireEvent("remove", this) !== false){
12336             this.reset();
12337             this.fireEvent("afterremove", this)
12338         }
12339     },
12340     
12341     createList : function()
12342     {
12343         this.list = Roo.get(document.body).createChild({
12344             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12345             cls: 'typeahead typeahead-long dropdown-menu shadow',
12346             style: 'display:none'
12347         });
12348         
12349         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12350         
12351     },
12352
12353     // private
12354     initTrigger : function(){
12355        
12356     },
12357
12358     // private
12359     onDestroy : function(){
12360         if(this.trigger){
12361             this.trigger.removeAllListeners();
12362           //  this.trigger.remove();
12363         }
12364         //if(this.wrap){
12365         //    this.wrap.remove();
12366         //}
12367         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12368     },
12369
12370     // private
12371     onFocus : function(){
12372         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12373         /*
12374         if(!this.mimicing){
12375             this.wrap.addClass('x-trigger-wrap-focus');
12376             this.mimicing = true;
12377             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12378             if(this.monitorTab){
12379                 this.el.on("keydown", this.checkTab, this);
12380             }
12381         }
12382         */
12383     },
12384
12385     // private
12386     checkTab : function(e){
12387         if(e.getKey() == e.TAB){
12388             this.triggerBlur();
12389         }
12390     },
12391
12392     // private
12393     onBlur : function(){
12394         // do nothing
12395     },
12396
12397     // private
12398     mimicBlur : function(e, t){
12399         /*
12400         if(!this.wrap.contains(t) && this.validateBlur()){
12401             this.triggerBlur();
12402         }
12403         */
12404     },
12405
12406     // private
12407     triggerBlur : function(){
12408         this.mimicing = false;
12409         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12410         if(this.monitorTab){
12411             this.el.un("keydown", this.checkTab, this);
12412         }
12413         //this.wrap.removeClass('x-trigger-wrap-focus');
12414         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12415     },
12416
12417     // private
12418     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12419     validateBlur : function(e, t){
12420         return true;
12421     },
12422
12423     // private
12424     onDisable : function(){
12425         this.inputEl().dom.disabled = true;
12426         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12427         //if(this.wrap){
12428         //    this.wrap.addClass('x-item-disabled');
12429         //}
12430     },
12431
12432     // private
12433     onEnable : function(){
12434         this.inputEl().dom.disabled = false;
12435         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12436         //if(this.wrap){
12437         //    this.el.removeClass('x-item-disabled');
12438         //}
12439     },
12440
12441     // private
12442     onShow : function(){
12443         var ae = this.getActionEl();
12444         
12445         if(ae){
12446             ae.dom.style.display = '';
12447             ae.dom.style.visibility = 'visible';
12448         }
12449     },
12450
12451     // private
12452     
12453     onHide : function(){
12454         var ae = this.getActionEl();
12455         ae.dom.style.display = 'none';
12456     },
12457
12458     /**
12459      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12460      * by an implementing function.
12461      * @method
12462      * @param {EventObject} e
12463      */
12464     onTriggerClick : Roo.emptyFn
12465 });
12466  
12467 /*
12468 * Licence: LGPL
12469 */
12470
12471 /**
12472  * @class Roo.bootstrap.CardUploader
12473  * @extends Roo.bootstrap.Button
12474  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12475  * @cfg {Number} errorTimeout default 3000
12476  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12477  * @cfg {Array}  html The button text.
12478
12479  *
12480  * @constructor
12481  * Create a new CardUploader
12482  * @param {Object} config The config object
12483  */
12484
12485 Roo.bootstrap.CardUploader = function(config){
12486     
12487  
12488     
12489     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12490     
12491     
12492     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12493         return r.data.id
12494         });
12495     
12496     
12497 };
12498
12499 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12500     
12501      
12502     errorTimeout : 3000,
12503      
12504     images : false,
12505    
12506     fileCollection : false,
12507     allowBlank : true,
12508     
12509     getAutoCreate : function()
12510     {
12511         
12512         var cfg =  {
12513             cls :'form-group' ,
12514             cn : [
12515                
12516                 {
12517                     tag: 'label',
12518                    //cls : 'input-group-addon',
12519                     html : this.fieldLabel
12520
12521                 },
12522
12523                 {
12524                     tag: 'input',
12525                     type : 'hidden',
12526                     value : this.value,
12527                     cls : 'd-none  form-control'
12528                 },
12529                 
12530                 {
12531                     tag: 'input',
12532                     multiple : 'multiple',
12533                     type : 'file',
12534                     cls : 'd-none  roo-card-upload-selector'
12535                 },
12536                 
12537                 {
12538                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12539                 },
12540                 {
12541                     cls : 'card-columns roo-card-uploader-container'
12542                 }
12543
12544             ]
12545         };
12546            
12547          
12548         return cfg;
12549     },
12550     
12551     getChildContainer : function() /// what children are added to.
12552     {
12553         return this.containerEl;
12554     },
12555    
12556     getButtonContainer : function() /// what children are added to.
12557     {
12558         return this.el.select(".roo-card-uploader-button-container").first();
12559     },
12560    
12561     initEvents : function()
12562     {
12563         
12564         Roo.bootstrap.Input.prototype.initEvents.call(this);
12565         
12566         var t = this;
12567         this.addxtype({
12568             xns: Roo.bootstrap,
12569
12570             xtype : 'Button',
12571             container_method : 'getButtonContainer' ,            
12572             html :  this.html, // fix changable?
12573             cls : 'w-100 ',
12574             listeners : {
12575                 'click' : function(btn, e) {
12576                     t.onClick(e);
12577                 }
12578             }
12579         });
12580         
12581         
12582         
12583         
12584         this.urlAPI = (window.createObjectURL && window) || 
12585                                 (window.URL && URL.revokeObjectURL && URL) || 
12586                                 (window.webkitURL && webkitURL);
12587                         
12588          
12589          
12590          
12591         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12592         
12593         this.selectorEl.on('change', this.onFileSelected, this);
12594         if (this.images) {
12595             var t = this;
12596             this.images.forEach(function(img) {
12597                 t.addCard(img)
12598             });
12599             this.images = false;
12600         }
12601         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12602          
12603        
12604     },
12605     
12606    
12607     onClick : function(e)
12608     {
12609         e.preventDefault();
12610          
12611         this.selectorEl.dom.click();
12612          
12613     },
12614     
12615     onFileSelected : function(e)
12616     {
12617         e.preventDefault();
12618         
12619         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12620             return;
12621         }
12622         
12623         Roo.each(this.selectorEl.dom.files, function(file){    
12624             this.addFile(file);
12625         }, this);
12626          
12627     },
12628     
12629       
12630     
12631       
12632     
12633     addFile : function(file)
12634     {
12635            
12636         if(typeof(file) === 'string'){
12637             throw "Add file by name?"; // should not happen
12638             return;
12639         }
12640         
12641         if(!file || !this.urlAPI){
12642             return;
12643         }
12644         
12645         // file;
12646         // file.type;
12647         
12648         var _this = this;
12649         
12650         
12651         var url = _this.urlAPI.createObjectURL( file);
12652            
12653         this.addCard({
12654             id : Roo.bootstrap.CardUploader.ID--,
12655             is_uploaded : false,
12656             src : url,
12657             title : file.name,
12658             mimetype : file.type,
12659             preview : false,
12660             is_deleted : 0
12661         })
12662         
12663     },
12664     
12665     addCard : function (data)
12666     {
12667         // hidden input element?
12668         // if the file is not an image...
12669         //then we need to use something other that and header_image
12670         var t = this;
12671         //   remove.....
12672         var footer = [
12673             {
12674                 xns : Roo.bootstrap,
12675                 xtype : 'CardFooter',
12676                 items: [
12677                     {
12678                         xns : Roo.bootstrap,
12679                         xtype : 'Element',
12680                         cls : 'd-flex',
12681                         items : [
12682                             
12683                             {
12684                                 xns : Roo.bootstrap,
12685                                 xtype : 'Button',
12686                                 html : String.format("<small>{0}</small>", data.title),
12687                                 cls : 'col-11 text-left',
12688                                 size: 'sm',
12689                                 weight: 'link',
12690                                 fa : 'download',
12691                                 listeners : {
12692                                     click : function() {
12693                                         this.downloadCard(data.id)
12694                                     }
12695                                 }
12696                             },
12697                           
12698                             {
12699                                 xns : Roo.bootstrap,
12700                                 xtype : 'Button',
12701                                 
12702                                 size : 'sm',
12703                                 weight: 'danger',
12704                                 cls : 'col-1',
12705                                 fa : 'times',
12706                                 listeners : {
12707                                     click : function() {
12708                                         t.removeCard(data.id)
12709                                     }
12710                                 }
12711                             }
12712                         ]
12713                     }
12714                     
12715                 ] 
12716             }
12717             
12718         ];
12719
12720         var cn = this.addxtype(
12721             {
12722                  
12723                 xns : Roo.bootstrap,
12724                 xtype : 'Card',
12725                 closeable : true,
12726                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12727                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12728                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12729                 data : data,
12730                 html : false,
12731                  
12732                 items : footer,
12733                 initEvents : function() {
12734                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12735                     this.imgEl = this.el.select('.card-img-top').first();
12736                     if (this.imgEl) {
12737                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12738                         this.imgEl.set({ 'pointer' : 'cursor' });
12739                                   
12740                     }
12741                     
12742                   
12743                 }
12744                 
12745             }
12746         );
12747         // dont' really need ot update items.
12748         // this.items.push(cn);
12749         this.fileCollection.add(cn);
12750         this.updateInput();
12751         
12752     },
12753     removeCard : function(id)
12754     {
12755         
12756         var card  = this.fileCollection.get(id);
12757         card.data.is_deleted = 1;
12758         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12759         this.fileCollection.remove(card);
12760         //this.items = this.items.filter(function(e) { return e != card });
12761         // dont' really need ot update items.
12762         card.el.dom.parentNode.removeChild(card.el.dom);
12763         
12764     },
12765     reset: function()
12766     {
12767         this.fileCollection.each(function(card) {
12768             card.el.dom.parentNode.removeChild(card.el.dom);    
12769         });
12770         this.fileCollection.clear();
12771         this.updateInput();
12772     },
12773     
12774     updateInput : function()
12775     {
12776         var data = [];
12777         this.fileCollection.each(function(e) {
12778             data.push(e.data);
12779         });
12780         
12781         this.inputEl().dom.value = JSON.stringify(data);
12782     }
12783     
12784     
12785 });
12786
12787
12788 Roo.bootstrap.CardUploader.ID = -1;/*
12789  * Based on:
12790  * Ext JS Library 1.1.1
12791  * Copyright(c) 2006-2007, Ext JS, LLC.
12792  *
12793  * Originally Released Under LGPL - original licence link has changed is not relivant.
12794  *
12795  * Fork - LGPL
12796  * <script type="text/javascript">
12797  */
12798
12799
12800 /**
12801  * @class Roo.data.SortTypes
12802  * @singleton
12803  * Defines the default sorting (casting?) comparison functions used when sorting data.
12804  */
12805 Roo.data.SortTypes = {
12806     /**
12807      * Default sort that does nothing
12808      * @param {Mixed} s The value being converted
12809      * @return {Mixed} The comparison value
12810      */
12811     none : function(s){
12812         return s;
12813     },
12814     
12815     /**
12816      * The regular expression used to strip tags
12817      * @type {RegExp}
12818      * @property
12819      */
12820     stripTagsRE : /<\/?[^>]+>/gi,
12821     
12822     /**
12823      * Strips all HTML tags to sort on text only
12824      * @param {Mixed} s The value being converted
12825      * @return {String} The comparison value
12826      */
12827     asText : function(s){
12828         return String(s).replace(this.stripTagsRE, "");
12829     },
12830     
12831     /**
12832      * Strips all HTML tags to sort on text only - Case insensitive
12833      * @param {Mixed} s The value being converted
12834      * @return {String} The comparison value
12835      */
12836     asUCText : function(s){
12837         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12838     },
12839     
12840     /**
12841      * Case insensitive string
12842      * @param {Mixed} s The value being converted
12843      * @return {String} The comparison value
12844      */
12845     asUCString : function(s) {
12846         return String(s).toUpperCase();
12847     },
12848     
12849     /**
12850      * Date sorting
12851      * @param {Mixed} s The value being converted
12852      * @return {Number} The comparison value
12853      */
12854     asDate : function(s) {
12855         if(!s){
12856             return 0;
12857         }
12858         if(s instanceof Date){
12859             return s.getTime();
12860         }
12861         return Date.parse(String(s));
12862     },
12863     
12864     /**
12865      * Float sorting
12866      * @param {Mixed} s The value being converted
12867      * @return {Float} The comparison value
12868      */
12869     asFloat : function(s) {
12870         var val = parseFloat(String(s).replace(/,/g, ""));
12871         if(isNaN(val)) {
12872             val = 0;
12873         }
12874         return val;
12875     },
12876     
12877     /**
12878      * Integer sorting
12879      * @param {Mixed} s The value being converted
12880      * @return {Number} The comparison value
12881      */
12882     asInt : function(s) {
12883         var val = parseInt(String(s).replace(/,/g, ""));
12884         if(isNaN(val)) {
12885             val = 0;
12886         }
12887         return val;
12888     }
12889 };/*
12890  * Based on:
12891  * Ext JS Library 1.1.1
12892  * Copyright(c) 2006-2007, Ext JS, LLC.
12893  *
12894  * Originally Released Under LGPL - original licence link has changed is not relivant.
12895  *
12896  * Fork - LGPL
12897  * <script type="text/javascript">
12898  */
12899
12900 /**
12901 * @class Roo.data.Record
12902  * Instances of this class encapsulate both record <em>definition</em> information, and record
12903  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12904  * to access Records cached in an {@link Roo.data.Store} object.<br>
12905  * <p>
12906  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12907  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12908  * objects.<br>
12909  * <p>
12910  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12911  * @constructor
12912  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12913  * {@link #create}. The parameters are the same.
12914  * @param {Array} data An associative Array of data values keyed by the field name.
12915  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12916  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12917  * not specified an integer id is generated.
12918  */
12919 Roo.data.Record = function(data, id){
12920     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12921     this.data = data;
12922 };
12923
12924 /**
12925  * Generate a constructor for a specific record layout.
12926  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12927  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12928  * Each field definition object may contain the following properties: <ul>
12929  * <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,
12930  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12931  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12932  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12933  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12934  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12935  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12936  * this may be omitted.</p></li>
12937  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12938  * <ul><li>auto (Default, implies no conversion)</li>
12939  * <li>string</li>
12940  * <li>int</li>
12941  * <li>float</li>
12942  * <li>boolean</li>
12943  * <li>date</li></ul></p></li>
12944  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12945  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12946  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12947  * by the Reader into an object that will be stored in the Record. It is passed the
12948  * following parameters:<ul>
12949  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12950  * </ul></p></li>
12951  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12952  * </ul>
12953  * <br>usage:<br><pre><code>
12954 var TopicRecord = Roo.data.Record.create(
12955     {name: 'title', mapping: 'topic_title'},
12956     {name: 'author', mapping: 'username'},
12957     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12958     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12959     {name: 'lastPoster', mapping: 'user2'},
12960     {name: 'excerpt', mapping: 'post_text'}
12961 );
12962
12963 var myNewRecord = new TopicRecord({
12964     title: 'Do my job please',
12965     author: 'noobie',
12966     totalPosts: 1,
12967     lastPost: new Date(),
12968     lastPoster: 'Animal',
12969     excerpt: 'No way dude!'
12970 });
12971 myStore.add(myNewRecord);
12972 </code></pre>
12973  * @method create
12974  * @static
12975  */
12976 Roo.data.Record.create = function(o){
12977     var f = function(){
12978         f.superclass.constructor.apply(this, arguments);
12979     };
12980     Roo.extend(f, Roo.data.Record);
12981     var p = f.prototype;
12982     p.fields = new Roo.util.MixedCollection(false, function(field){
12983         return field.name;
12984     });
12985     for(var i = 0, len = o.length; i < len; i++){
12986         p.fields.add(new Roo.data.Field(o[i]));
12987     }
12988     f.getField = function(name){
12989         return p.fields.get(name);  
12990     };
12991     return f;
12992 };
12993
12994 Roo.data.Record.AUTO_ID = 1000;
12995 Roo.data.Record.EDIT = 'edit';
12996 Roo.data.Record.REJECT = 'reject';
12997 Roo.data.Record.COMMIT = 'commit';
12998
12999 Roo.data.Record.prototype = {
13000     /**
13001      * Readonly flag - true if this record has been modified.
13002      * @type Boolean
13003      */
13004     dirty : false,
13005     editing : false,
13006     error: null,
13007     modified: null,
13008
13009     // private
13010     join : function(store){
13011         this.store = store;
13012     },
13013
13014     /**
13015      * Set the named field to the specified value.
13016      * @param {String} name The name of the field to set.
13017      * @param {Object} value The value to set the field to.
13018      */
13019     set : function(name, value){
13020         if(this.data[name] == value){
13021             return;
13022         }
13023         this.dirty = true;
13024         if(!this.modified){
13025             this.modified = {};
13026         }
13027         if(typeof this.modified[name] == 'undefined'){
13028             this.modified[name] = this.data[name];
13029         }
13030         this.data[name] = value;
13031         if(!this.editing && this.store){
13032             this.store.afterEdit(this);
13033         }       
13034     },
13035
13036     /**
13037      * Get the value of the named field.
13038      * @param {String} name The name of the field to get the value of.
13039      * @return {Object} The value of the field.
13040      */
13041     get : function(name){
13042         return this.data[name]; 
13043     },
13044
13045     // private
13046     beginEdit : function(){
13047         this.editing = true;
13048         this.modified = {}; 
13049     },
13050
13051     // private
13052     cancelEdit : function(){
13053         this.editing = false;
13054         delete this.modified;
13055     },
13056
13057     // private
13058     endEdit : function(){
13059         this.editing = false;
13060         if(this.dirty && this.store){
13061             this.store.afterEdit(this);
13062         }
13063     },
13064
13065     /**
13066      * Usually called by the {@link Roo.data.Store} which owns the Record.
13067      * Rejects all changes made to the Record since either creation, or the last commit operation.
13068      * Modified fields are reverted to their original values.
13069      * <p>
13070      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13071      * of reject operations.
13072      */
13073     reject : function(){
13074         var m = this.modified;
13075         for(var n in m){
13076             if(typeof m[n] != "function"){
13077                 this.data[n] = m[n];
13078             }
13079         }
13080         this.dirty = false;
13081         delete this.modified;
13082         this.editing = false;
13083         if(this.store){
13084             this.store.afterReject(this);
13085         }
13086     },
13087
13088     /**
13089      * Usually called by the {@link Roo.data.Store} which owns the Record.
13090      * Commits all changes made to the Record since either creation, or the last commit operation.
13091      * <p>
13092      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13093      * of commit operations.
13094      */
13095     commit : function(){
13096         this.dirty = false;
13097         delete this.modified;
13098         this.editing = false;
13099         if(this.store){
13100             this.store.afterCommit(this);
13101         }
13102     },
13103
13104     // private
13105     hasError : function(){
13106         return this.error != null;
13107     },
13108
13109     // private
13110     clearError : function(){
13111         this.error = null;
13112     },
13113
13114     /**
13115      * Creates a copy of this record.
13116      * @param {String} id (optional) A new record id if you don't want to use this record's id
13117      * @return {Record}
13118      */
13119     copy : function(newId) {
13120         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13121     }
13122 };/*
13123  * Based on:
13124  * Ext JS Library 1.1.1
13125  * Copyright(c) 2006-2007, Ext JS, LLC.
13126  *
13127  * Originally Released Under LGPL - original licence link has changed is not relivant.
13128  *
13129  * Fork - LGPL
13130  * <script type="text/javascript">
13131  */
13132
13133
13134
13135 /**
13136  * @class Roo.data.Store
13137  * @extends Roo.util.Observable
13138  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13139  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13140  * <p>
13141  * 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
13142  * has no knowledge of the format of the data returned by the Proxy.<br>
13143  * <p>
13144  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13145  * instances from the data object. These records are cached and made available through accessor functions.
13146  * @constructor
13147  * Creates a new Store.
13148  * @param {Object} config A config object containing the objects needed for the Store to access data,
13149  * and read the data into Records.
13150  */
13151 Roo.data.Store = function(config){
13152     this.data = new Roo.util.MixedCollection(false);
13153     this.data.getKey = function(o){
13154         return o.id;
13155     };
13156     this.baseParams = {};
13157     // private
13158     this.paramNames = {
13159         "start" : "start",
13160         "limit" : "limit",
13161         "sort" : "sort",
13162         "dir" : "dir",
13163         "multisort" : "_multisort"
13164     };
13165
13166     if(config && config.data){
13167         this.inlineData = config.data;
13168         delete config.data;
13169     }
13170
13171     Roo.apply(this, config);
13172     
13173     if(this.reader){ // reader passed
13174         this.reader = Roo.factory(this.reader, Roo.data);
13175         this.reader.xmodule = this.xmodule || false;
13176         if(!this.recordType){
13177             this.recordType = this.reader.recordType;
13178         }
13179         if(this.reader.onMetaChange){
13180             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13181         }
13182     }
13183
13184     if(this.recordType){
13185         this.fields = this.recordType.prototype.fields;
13186     }
13187     this.modified = [];
13188
13189     this.addEvents({
13190         /**
13191          * @event datachanged
13192          * Fires when the data cache has changed, and a widget which is using this Store
13193          * as a Record cache should refresh its view.
13194          * @param {Store} this
13195          */
13196         datachanged : true,
13197         /**
13198          * @event metachange
13199          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13200          * @param {Store} this
13201          * @param {Object} meta The JSON metadata
13202          */
13203         metachange : true,
13204         /**
13205          * @event add
13206          * Fires when Records have been added to the Store
13207          * @param {Store} this
13208          * @param {Roo.data.Record[]} records The array of Records added
13209          * @param {Number} index The index at which the record(s) were added
13210          */
13211         add : true,
13212         /**
13213          * @event remove
13214          * Fires when a Record has been removed from the Store
13215          * @param {Store} this
13216          * @param {Roo.data.Record} record The Record that was removed
13217          * @param {Number} index The index at which the record was removed
13218          */
13219         remove : true,
13220         /**
13221          * @event update
13222          * Fires when a Record has been updated
13223          * @param {Store} this
13224          * @param {Roo.data.Record} record The Record that was updated
13225          * @param {String} operation The update operation being performed.  Value may be one of:
13226          * <pre><code>
13227  Roo.data.Record.EDIT
13228  Roo.data.Record.REJECT
13229  Roo.data.Record.COMMIT
13230          * </code></pre>
13231          */
13232         update : true,
13233         /**
13234          * @event clear
13235          * Fires when the data cache has been cleared.
13236          * @param {Store} this
13237          */
13238         clear : true,
13239         /**
13240          * @event beforeload
13241          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13242          * the load action will be canceled.
13243          * @param {Store} this
13244          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13245          */
13246         beforeload : true,
13247         /**
13248          * @event beforeloadadd
13249          * Fires after a new set of Records has been loaded.
13250          * @param {Store} this
13251          * @param {Roo.data.Record[]} records The Records that were loaded
13252          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13253          */
13254         beforeloadadd : true,
13255         /**
13256          * @event load
13257          * Fires after a new set of Records has been loaded, before they are added to the store.
13258          * @param {Store} this
13259          * @param {Roo.data.Record[]} records The Records that were loaded
13260          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13261          * @params {Object} return from reader
13262          */
13263         load : true,
13264         /**
13265          * @event loadexception
13266          * Fires if an exception occurs in the Proxy during loading.
13267          * Called with the signature of the Proxy's "loadexception" event.
13268          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13269          * 
13270          * @param {Proxy} 
13271          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13272          * @param {Object} load options 
13273          * @param {Object} jsonData from your request (normally this contains the Exception)
13274          */
13275         loadexception : true
13276     });
13277     
13278     if(this.proxy){
13279         this.proxy = Roo.factory(this.proxy, Roo.data);
13280         this.proxy.xmodule = this.xmodule || false;
13281         this.relayEvents(this.proxy,  ["loadexception"]);
13282     }
13283     this.sortToggle = {};
13284     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13285
13286     Roo.data.Store.superclass.constructor.call(this);
13287
13288     if(this.inlineData){
13289         this.loadData(this.inlineData);
13290         delete this.inlineData;
13291     }
13292 };
13293
13294 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13295      /**
13296     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13297     * without a remote query - used by combo/forms at present.
13298     */
13299     
13300     /**
13301     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13302     */
13303     /**
13304     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13305     */
13306     /**
13307     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13308     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13309     */
13310     /**
13311     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13312     * on any HTTP request
13313     */
13314     /**
13315     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13316     */
13317     /**
13318     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13319     */
13320     multiSort: false,
13321     /**
13322     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13323     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13324     */
13325     remoteSort : false,
13326
13327     /**
13328     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13329      * loaded or when a record is removed. (defaults to false).
13330     */
13331     pruneModifiedRecords : false,
13332
13333     // private
13334     lastOptions : null,
13335
13336     /**
13337      * Add Records to the Store and fires the add event.
13338      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13339      */
13340     add : function(records){
13341         records = [].concat(records);
13342         for(var i = 0, len = records.length; i < len; i++){
13343             records[i].join(this);
13344         }
13345         var index = this.data.length;
13346         this.data.addAll(records);
13347         this.fireEvent("add", this, records, index);
13348     },
13349
13350     /**
13351      * Remove a Record from the Store and fires the remove event.
13352      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13353      */
13354     remove : function(record){
13355         var index = this.data.indexOf(record);
13356         this.data.removeAt(index);
13357  
13358         if(this.pruneModifiedRecords){
13359             this.modified.remove(record);
13360         }
13361         this.fireEvent("remove", this, record, index);
13362     },
13363
13364     /**
13365      * Remove all Records from the Store and fires the clear event.
13366      */
13367     removeAll : function(){
13368         this.data.clear();
13369         if(this.pruneModifiedRecords){
13370             this.modified = [];
13371         }
13372         this.fireEvent("clear", this);
13373     },
13374
13375     /**
13376      * Inserts Records to the Store at the given index and fires the add event.
13377      * @param {Number} index The start index at which to insert the passed Records.
13378      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13379      */
13380     insert : function(index, records){
13381         records = [].concat(records);
13382         for(var i = 0, len = records.length; i < len; i++){
13383             this.data.insert(index, records[i]);
13384             records[i].join(this);
13385         }
13386         this.fireEvent("add", this, records, index);
13387     },
13388
13389     /**
13390      * Get the index within the cache of the passed Record.
13391      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13392      * @return {Number} The index of the passed Record. Returns -1 if not found.
13393      */
13394     indexOf : function(record){
13395         return this.data.indexOf(record);
13396     },
13397
13398     /**
13399      * Get the index within the cache of the Record with the passed id.
13400      * @param {String} id The id of the Record to find.
13401      * @return {Number} The index of the Record. Returns -1 if not found.
13402      */
13403     indexOfId : function(id){
13404         return this.data.indexOfKey(id);
13405     },
13406
13407     /**
13408      * Get the Record with the specified id.
13409      * @param {String} id The id of the Record to find.
13410      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13411      */
13412     getById : function(id){
13413         return this.data.key(id);
13414     },
13415
13416     /**
13417      * Get the Record at the specified index.
13418      * @param {Number} index The index of the Record to find.
13419      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13420      */
13421     getAt : function(index){
13422         return this.data.itemAt(index);
13423     },
13424
13425     /**
13426      * Returns a range of Records between specified indices.
13427      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13428      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13429      * @return {Roo.data.Record[]} An array of Records
13430      */
13431     getRange : function(start, end){
13432         return this.data.getRange(start, end);
13433     },
13434
13435     // private
13436     storeOptions : function(o){
13437         o = Roo.apply({}, o);
13438         delete o.callback;
13439         delete o.scope;
13440         this.lastOptions = o;
13441     },
13442
13443     /**
13444      * Loads the Record cache from the configured Proxy using the configured Reader.
13445      * <p>
13446      * If using remote paging, then the first load call must specify the <em>start</em>
13447      * and <em>limit</em> properties in the options.params property to establish the initial
13448      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13449      * <p>
13450      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13451      * and this call will return before the new data has been loaded. Perform any post-processing
13452      * in a callback function, or in a "load" event handler.</strong>
13453      * <p>
13454      * @param {Object} options An object containing properties which control loading options:<ul>
13455      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13456      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13457      * passed the following arguments:<ul>
13458      * <li>r : Roo.data.Record[]</li>
13459      * <li>options: Options object from the load call</li>
13460      * <li>success: Boolean success indicator</li></ul></li>
13461      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13462      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13463      * </ul>
13464      */
13465     load : function(options){
13466         options = options || {};
13467         if(this.fireEvent("beforeload", this, options) !== false){
13468             this.storeOptions(options);
13469             var p = Roo.apply(options.params || {}, this.baseParams);
13470             // if meta was not loaded from remote source.. try requesting it.
13471             if (!this.reader.metaFromRemote) {
13472                 p._requestMeta = 1;
13473             }
13474             if(this.sortInfo && this.remoteSort){
13475                 var pn = this.paramNames;
13476                 p[pn["sort"]] = this.sortInfo.field;
13477                 p[pn["dir"]] = this.sortInfo.direction;
13478             }
13479             if (this.multiSort) {
13480                 var pn = this.paramNames;
13481                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13482             }
13483             
13484             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13485         }
13486     },
13487
13488     /**
13489      * Reloads the Record cache from the configured Proxy using the configured Reader and
13490      * the options from the last load operation performed.
13491      * @param {Object} options (optional) An object containing properties which may override the options
13492      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13493      * the most recently used options are reused).
13494      */
13495     reload : function(options){
13496         this.load(Roo.applyIf(options||{}, this.lastOptions));
13497     },
13498
13499     // private
13500     // Called as a callback by the Reader during a load operation.
13501     loadRecords : function(o, options, success){
13502         if(!o || success === false){
13503             if(success !== false){
13504                 this.fireEvent("load", this, [], options, o);
13505             }
13506             if(options.callback){
13507                 options.callback.call(options.scope || this, [], options, false);
13508             }
13509             return;
13510         }
13511         // if data returned failure - throw an exception.
13512         if (o.success === false) {
13513             // show a message if no listener is registered.
13514             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13515                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13516             }
13517             // loadmask wil be hooked into this..
13518             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13519             return;
13520         }
13521         var r = o.records, t = o.totalRecords || r.length;
13522         
13523         this.fireEvent("beforeloadadd", this, r, options, o);
13524         
13525         if(!options || options.add !== true){
13526             if(this.pruneModifiedRecords){
13527                 this.modified = [];
13528             }
13529             for(var i = 0, len = r.length; i < len; i++){
13530                 r[i].join(this);
13531             }
13532             if(this.snapshot){
13533                 this.data = this.snapshot;
13534                 delete this.snapshot;
13535             }
13536             this.data.clear();
13537             this.data.addAll(r);
13538             this.totalLength = t;
13539             this.applySort();
13540             this.fireEvent("datachanged", this);
13541         }else{
13542             this.totalLength = Math.max(t, this.data.length+r.length);
13543             this.add(r);
13544         }
13545         
13546         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13547                 
13548             var e = new Roo.data.Record({});
13549
13550             e.set(this.parent.displayField, this.parent.emptyTitle);
13551             e.set(this.parent.valueField, '');
13552
13553             this.insert(0, e);
13554         }
13555             
13556         this.fireEvent("load", this, r, options, o);
13557         if(options.callback){
13558             options.callback.call(options.scope || this, r, options, true);
13559         }
13560     },
13561
13562
13563     /**
13564      * Loads data from a passed data block. A Reader which understands the format of the data
13565      * must have been configured in the constructor.
13566      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13567      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13568      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13569      */
13570     loadData : function(o, append){
13571         var r = this.reader.readRecords(o);
13572         this.loadRecords(r, {add: append}, true);
13573     },
13574     
13575      /**
13576      * using 'cn' the nested child reader read the child array into it's child stores.
13577      * @param {Object} rec The record with a 'children array
13578      */
13579     loadDataFromChildren : function(rec)
13580     {
13581         this.loadData(this.reader.toLoadData(rec));
13582     },
13583     
13584
13585     /**
13586      * Gets the number of cached records.
13587      * <p>
13588      * <em>If using paging, this may not be the total size of the dataset. If the data object
13589      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13590      * the data set size</em>
13591      */
13592     getCount : function(){
13593         return this.data.length || 0;
13594     },
13595
13596     /**
13597      * Gets the total number of records in the dataset as returned by the server.
13598      * <p>
13599      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13600      * the dataset size</em>
13601      */
13602     getTotalCount : function(){
13603         return this.totalLength || 0;
13604     },
13605
13606     /**
13607      * Returns the sort state of the Store as an object with two properties:
13608      * <pre><code>
13609  field {String} The name of the field by which the Records are sorted
13610  direction {String} The sort order, "ASC" or "DESC"
13611      * </code></pre>
13612      */
13613     getSortState : function(){
13614         return this.sortInfo;
13615     },
13616
13617     // private
13618     applySort : function(){
13619         if(this.sortInfo && !this.remoteSort){
13620             var s = this.sortInfo, f = s.field;
13621             var st = this.fields.get(f).sortType;
13622             var fn = function(r1, r2){
13623                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13624                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13625             };
13626             this.data.sort(s.direction, fn);
13627             if(this.snapshot && this.snapshot != this.data){
13628                 this.snapshot.sort(s.direction, fn);
13629             }
13630         }
13631     },
13632
13633     /**
13634      * Sets the default sort column and order to be used by the next load operation.
13635      * @param {String} fieldName The name of the field to sort by.
13636      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13637      */
13638     setDefaultSort : function(field, dir){
13639         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13640     },
13641
13642     /**
13643      * Sort the Records.
13644      * If remote sorting is used, the sort is performed on the server, and the cache is
13645      * reloaded. If local sorting is used, the cache is sorted internally.
13646      * @param {String} fieldName The name of the field to sort by.
13647      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13648      */
13649     sort : function(fieldName, dir){
13650         var f = this.fields.get(fieldName);
13651         if(!dir){
13652             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13653             
13654             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13655                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13656             }else{
13657                 dir = f.sortDir;
13658             }
13659         }
13660         this.sortToggle[f.name] = dir;
13661         this.sortInfo = {field: f.name, direction: dir};
13662         if(!this.remoteSort){
13663             this.applySort();
13664             this.fireEvent("datachanged", this);
13665         }else{
13666             this.load(this.lastOptions);
13667         }
13668     },
13669
13670     /**
13671      * Calls the specified function for each of the Records in the cache.
13672      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13673      * Returning <em>false</em> aborts and exits the iteration.
13674      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13675      */
13676     each : function(fn, scope){
13677         this.data.each(fn, scope);
13678     },
13679
13680     /**
13681      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13682      * (e.g., during paging).
13683      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13684      */
13685     getModifiedRecords : function(){
13686         return this.modified;
13687     },
13688
13689     // private
13690     createFilterFn : function(property, value, anyMatch){
13691         if(!value.exec){ // not a regex
13692             value = String(value);
13693             if(value.length == 0){
13694                 return false;
13695             }
13696             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13697         }
13698         return function(r){
13699             return value.test(r.data[property]);
13700         };
13701     },
13702
13703     /**
13704      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13705      * @param {String} property A field on your records
13706      * @param {Number} start The record index to start at (defaults to 0)
13707      * @param {Number} end The last record index to include (defaults to length - 1)
13708      * @return {Number} The sum
13709      */
13710     sum : function(property, start, end){
13711         var rs = this.data.items, v = 0;
13712         start = start || 0;
13713         end = (end || end === 0) ? end : rs.length-1;
13714
13715         for(var i = start; i <= end; i++){
13716             v += (rs[i].data[property] || 0);
13717         }
13718         return v;
13719     },
13720
13721     /**
13722      * Filter the records by a specified property.
13723      * @param {String} field A field on your records
13724      * @param {String/RegExp} value Either a string that the field
13725      * should start with or a RegExp to test against the field
13726      * @param {Boolean} anyMatch True to match any part not just the beginning
13727      */
13728     filter : function(property, value, anyMatch){
13729         var fn = this.createFilterFn(property, value, anyMatch);
13730         return fn ? this.filterBy(fn) : this.clearFilter();
13731     },
13732
13733     /**
13734      * Filter by a function. The specified function will be called with each
13735      * record in this data source. If the function returns true the record is included,
13736      * otherwise it is filtered.
13737      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13738      * @param {Object} scope (optional) The scope of the function (defaults to this)
13739      */
13740     filterBy : function(fn, scope){
13741         this.snapshot = this.snapshot || this.data;
13742         this.data = this.queryBy(fn, scope||this);
13743         this.fireEvent("datachanged", this);
13744     },
13745
13746     /**
13747      * Query the records by a specified property.
13748      * @param {String} field A field on your records
13749      * @param {String/RegExp} value Either a string that the field
13750      * should start with or a RegExp to test against the field
13751      * @param {Boolean} anyMatch True to match any part not just the beginning
13752      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13753      */
13754     query : function(property, value, anyMatch){
13755         var fn = this.createFilterFn(property, value, anyMatch);
13756         return fn ? this.queryBy(fn) : this.data.clone();
13757     },
13758
13759     /**
13760      * Query by a function. The specified function will be called with each
13761      * record in this data source. If the function returns true the record is included
13762      * in the results.
13763      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13764      * @param {Object} scope (optional) The scope of the function (defaults to this)
13765       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13766      **/
13767     queryBy : function(fn, scope){
13768         var data = this.snapshot || this.data;
13769         return data.filterBy(fn, scope||this);
13770     },
13771
13772     /**
13773      * Collects unique values for a particular dataIndex from this store.
13774      * @param {String} dataIndex The property to collect
13775      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13776      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13777      * @return {Array} An array of the unique values
13778      **/
13779     collect : function(dataIndex, allowNull, bypassFilter){
13780         var d = (bypassFilter === true && this.snapshot) ?
13781                 this.snapshot.items : this.data.items;
13782         var v, sv, r = [], l = {};
13783         for(var i = 0, len = d.length; i < len; i++){
13784             v = d[i].data[dataIndex];
13785             sv = String(v);
13786             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13787                 l[sv] = true;
13788                 r[r.length] = v;
13789             }
13790         }
13791         return r;
13792     },
13793
13794     /**
13795      * Revert to a view of the Record cache with no filtering applied.
13796      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13797      */
13798     clearFilter : function(suppressEvent){
13799         if(this.snapshot && this.snapshot != this.data){
13800             this.data = this.snapshot;
13801             delete this.snapshot;
13802             if(suppressEvent !== true){
13803                 this.fireEvent("datachanged", this);
13804             }
13805         }
13806     },
13807
13808     // private
13809     afterEdit : function(record){
13810         if(this.modified.indexOf(record) == -1){
13811             this.modified.push(record);
13812         }
13813         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13814     },
13815     
13816     // private
13817     afterReject : function(record){
13818         this.modified.remove(record);
13819         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13820     },
13821
13822     // private
13823     afterCommit : function(record){
13824         this.modified.remove(record);
13825         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13826     },
13827
13828     /**
13829      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13830      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13831      */
13832     commitChanges : function(){
13833         var m = this.modified.slice(0);
13834         this.modified = [];
13835         for(var i = 0, len = m.length; i < len; i++){
13836             m[i].commit();
13837         }
13838     },
13839
13840     /**
13841      * Cancel outstanding changes on all changed records.
13842      */
13843     rejectChanges : function(){
13844         var m = this.modified.slice(0);
13845         this.modified = [];
13846         for(var i = 0, len = m.length; i < len; i++){
13847             m[i].reject();
13848         }
13849     },
13850
13851     onMetaChange : function(meta, rtype, o){
13852         this.recordType = rtype;
13853         this.fields = rtype.prototype.fields;
13854         delete this.snapshot;
13855         this.sortInfo = meta.sortInfo || this.sortInfo;
13856         this.modified = [];
13857         this.fireEvent('metachange', this, this.reader.meta);
13858     },
13859     
13860     moveIndex : function(data, type)
13861     {
13862         var index = this.indexOf(data);
13863         
13864         var newIndex = index + type;
13865         
13866         this.remove(data);
13867         
13868         this.insert(newIndex, data);
13869         
13870     }
13871 });/*
13872  * Based on:
13873  * Ext JS Library 1.1.1
13874  * Copyright(c) 2006-2007, Ext JS, LLC.
13875  *
13876  * Originally Released Under LGPL - original licence link has changed is not relivant.
13877  *
13878  * Fork - LGPL
13879  * <script type="text/javascript">
13880  */
13881
13882 /**
13883  * @class Roo.data.SimpleStore
13884  * @extends Roo.data.Store
13885  * Small helper class to make creating Stores from Array data easier.
13886  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13887  * @cfg {Array} fields An array of field definition objects, or field name strings.
13888  * @cfg {Object} an existing reader (eg. copied from another store)
13889  * @cfg {Array} data The multi-dimensional array of data
13890  * @constructor
13891  * @param {Object} config
13892  */
13893 Roo.data.SimpleStore = function(config)
13894 {
13895     Roo.data.SimpleStore.superclass.constructor.call(this, {
13896         isLocal : true,
13897         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13898                 id: config.id
13899             },
13900             Roo.data.Record.create(config.fields)
13901         ),
13902         proxy : new Roo.data.MemoryProxy(config.data)
13903     });
13904     this.load();
13905 };
13906 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13907  * Based on:
13908  * Ext JS Library 1.1.1
13909  * Copyright(c) 2006-2007, Ext JS, LLC.
13910  *
13911  * Originally Released Under LGPL - original licence link has changed is not relivant.
13912  *
13913  * Fork - LGPL
13914  * <script type="text/javascript">
13915  */
13916
13917 /**
13918 /**
13919  * @extends Roo.data.Store
13920  * @class Roo.data.JsonStore
13921  * Small helper class to make creating Stores for JSON data easier. <br/>
13922 <pre><code>
13923 var store = new Roo.data.JsonStore({
13924     url: 'get-images.php',
13925     root: 'images',
13926     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13927 });
13928 </code></pre>
13929  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13930  * JsonReader and HttpProxy (unless inline data is provided).</b>
13931  * @cfg {Array} fields An array of field definition objects, or field name strings.
13932  * @constructor
13933  * @param {Object} config
13934  */
13935 Roo.data.JsonStore = function(c){
13936     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13937         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13938         reader: new Roo.data.JsonReader(c, c.fields)
13939     }));
13940 };
13941 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13942  * Based on:
13943  * Ext JS Library 1.1.1
13944  * Copyright(c) 2006-2007, Ext JS, LLC.
13945  *
13946  * Originally Released Under LGPL - original licence link has changed is not relivant.
13947  *
13948  * Fork - LGPL
13949  * <script type="text/javascript">
13950  */
13951
13952  
13953 Roo.data.Field = function(config){
13954     if(typeof config == "string"){
13955         config = {name: config};
13956     }
13957     Roo.apply(this, config);
13958     
13959     if(!this.type){
13960         this.type = "auto";
13961     }
13962     
13963     var st = Roo.data.SortTypes;
13964     // named sortTypes are supported, here we look them up
13965     if(typeof this.sortType == "string"){
13966         this.sortType = st[this.sortType];
13967     }
13968     
13969     // set default sortType for strings and dates
13970     if(!this.sortType){
13971         switch(this.type){
13972             case "string":
13973                 this.sortType = st.asUCString;
13974                 break;
13975             case "date":
13976                 this.sortType = st.asDate;
13977                 break;
13978             default:
13979                 this.sortType = st.none;
13980         }
13981     }
13982
13983     // define once
13984     var stripRe = /[\$,%]/g;
13985
13986     // prebuilt conversion function for this field, instead of
13987     // switching every time we're reading a value
13988     if(!this.convert){
13989         var cv, dateFormat = this.dateFormat;
13990         switch(this.type){
13991             case "":
13992             case "auto":
13993             case undefined:
13994                 cv = function(v){ return v; };
13995                 break;
13996             case "string":
13997                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13998                 break;
13999             case "int":
14000                 cv = function(v){
14001                     return v !== undefined && v !== null && v !== '' ?
14002                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14003                     };
14004                 break;
14005             case "float":
14006                 cv = function(v){
14007                     return v !== undefined && v !== null && v !== '' ?
14008                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14009                     };
14010                 break;
14011             case "bool":
14012             case "boolean":
14013                 cv = function(v){ return v === true || v === "true" || v == 1; };
14014                 break;
14015             case "date":
14016                 cv = function(v){
14017                     if(!v){
14018                         return '';
14019                     }
14020                     if(v instanceof Date){
14021                         return v;
14022                     }
14023                     if(dateFormat){
14024                         if(dateFormat == "timestamp"){
14025                             return new Date(v*1000);
14026                         }
14027                         return Date.parseDate(v, dateFormat);
14028                     }
14029                     var parsed = Date.parse(v);
14030                     return parsed ? new Date(parsed) : null;
14031                 };
14032              break;
14033             
14034         }
14035         this.convert = cv;
14036     }
14037 };
14038
14039 Roo.data.Field.prototype = {
14040     dateFormat: null,
14041     defaultValue: "",
14042     mapping: null,
14043     sortType : null,
14044     sortDir : "ASC"
14045 };/*
14046  * Based on:
14047  * Ext JS Library 1.1.1
14048  * Copyright(c) 2006-2007, Ext JS, LLC.
14049  *
14050  * Originally Released Under LGPL - original licence link has changed is not relivant.
14051  *
14052  * Fork - LGPL
14053  * <script type="text/javascript">
14054  */
14055  
14056 // Base class for reading structured data from a data source.  This class is intended to be
14057 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14058
14059 /**
14060  * @class Roo.data.DataReader
14061  * Base class for reading structured data from a data source.  This class is intended to be
14062  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14063  */
14064
14065 Roo.data.DataReader = function(meta, recordType){
14066     
14067     this.meta = meta;
14068     
14069     this.recordType = recordType instanceof Array ? 
14070         Roo.data.Record.create(recordType) : recordType;
14071 };
14072
14073 Roo.data.DataReader.prototype = {
14074     
14075     
14076     readerType : 'Data',
14077      /**
14078      * Create an empty record
14079      * @param {Object} data (optional) - overlay some values
14080      * @return {Roo.data.Record} record created.
14081      */
14082     newRow :  function(d) {
14083         var da =  {};
14084         this.recordType.prototype.fields.each(function(c) {
14085             switch( c.type) {
14086                 case 'int' : da[c.name] = 0; break;
14087                 case 'date' : da[c.name] = new Date(); break;
14088                 case 'float' : da[c.name] = 0.0; break;
14089                 case 'boolean' : da[c.name] = false; break;
14090                 default : da[c.name] = ""; break;
14091             }
14092             
14093         });
14094         return new this.recordType(Roo.apply(da, d));
14095     }
14096     
14097     
14098 };/*
14099  * Based on:
14100  * Ext JS Library 1.1.1
14101  * Copyright(c) 2006-2007, Ext JS, LLC.
14102  *
14103  * Originally Released Under LGPL - original licence link has changed is not relivant.
14104  *
14105  * Fork - LGPL
14106  * <script type="text/javascript">
14107  */
14108
14109 /**
14110  * @class Roo.data.DataProxy
14111  * @extends Roo.data.Observable
14112  * This class is an abstract base class for implementations which provide retrieval of
14113  * unformatted data objects.<br>
14114  * <p>
14115  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14116  * (of the appropriate type which knows how to parse the data object) to provide a block of
14117  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14118  * <p>
14119  * Custom implementations must implement the load method as described in
14120  * {@link Roo.data.HttpProxy#load}.
14121  */
14122 Roo.data.DataProxy = function(){
14123     this.addEvents({
14124         /**
14125          * @event beforeload
14126          * Fires before a network request is made to retrieve a data object.
14127          * @param {Object} This DataProxy object.
14128          * @param {Object} params The params parameter to the load function.
14129          */
14130         beforeload : true,
14131         /**
14132          * @event load
14133          * Fires before the load method's callback is called.
14134          * @param {Object} This DataProxy object.
14135          * @param {Object} o The data object.
14136          * @param {Object} arg The callback argument object passed to the load function.
14137          */
14138         load : true,
14139         /**
14140          * @event loadexception
14141          * Fires if an Exception occurs during data retrieval.
14142          * @param {Object} This DataProxy object.
14143          * @param {Object} o The data object.
14144          * @param {Object} arg The callback argument object passed to the load function.
14145          * @param {Object} e The Exception.
14146          */
14147         loadexception : true
14148     });
14149     Roo.data.DataProxy.superclass.constructor.call(this);
14150 };
14151
14152 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14153
14154     /**
14155      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14156      */
14157 /*
14158  * Based on:
14159  * Ext JS Library 1.1.1
14160  * Copyright(c) 2006-2007, Ext JS, LLC.
14161  *
14162  * Originally Released Under LGPL - original licence link has changed is not relivant.
14163  *
14164  * Fork - LGPL
14165  * <script type="text/javascript">
14166  */
14167 /**
14168  * @class Roo.data.MemoryProxy
14169  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14170  * to the Reader when its load method is called.
14171  * @constructor
14172  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14173  */
14174 Roo.data.MemoryProxy = function(data){
14175     if (data.data) {
14176         data = data.data;
14177     }
14178     Roo.data.MemoryProxy.superclass.constructor.call(this);
14179     this.data = data;
14180 };
14181
14182 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14183     
14184     /**
14185      * Load data from the requested source (in this case an in-memory
14186      * data object passed to the constructor), read the data object into
14187      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14188      * process that block using the passed callback.
14189      * @param {Object} params This parameter is not used by the MemoryProxy class.
14190      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14191      * object into a block of Roo.data.Records.
14192      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14193      * The function must be passed <ul>
14194      * <li>The Record block object</li>
14195      * <li>The "arg" argument from the load function</li>
14196      * <li>A boolean success indicator</li>
14197      * </ul>
14198      * @param {Object} scope The scope in which to call the callback
14199      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14200      */
14201     load : function(params, reader, callback, scope, arg){
14202         params = params || {};
14203         var result;
14204         try {
14205             result = reader.readRecords(params.data ? params.data :this.data);
14206         }catch(e){
14207             this.fireEvent("loadexception", this, arg, null, e);
14208             callback.call(scope, null, arg, false);
14209             return;
14210         }
14211         callback.call(scope, result, arg, true);
14212     },
14213     
14214     // private
14215     update : function(params, records){
14216         
14217     }
14218 });/*
14219  * Based on:
14220  * Ext JS Library 1.1.1
14221  * Copyright(c) 2006-2007, Ext JS, LLC.
14222  *
14223  * Originally Released Under LGPL - original licence link has changed is not relivant.
14224  *
14225  * Fork - LGPL
14226  * <script type="text/javascript">
14227  */
14228 /**
14229  * @class Roo.data.HttpProxy
14230  * @extends Roo.data.DataProxy
14231  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14232  * configured to reference a certain URL.<br><br>
14233  * <p>
14234  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14235  * from which the running page was served.<br><br>
14236  * <p>
14237  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14238  * <p>
14239  * Be aware that to enable the browser to parse an XML document, the server must set
14240  * the Content-Type header in the HTTP response to "text/xml".
14241  * @constructor
14242  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14243  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14244  * will be used to make the request.
14245  */
14246 Roo.data.HttpProxy = function(conn){
14247     Roo.data.HttpProxy.superclass.constructor.call(this);
14248     // is conn a conn config or a real conn?
14249     this.conn = conn;
14250     this.useAjax = !conn || !conn.events;
14251   
14252 };
14253
14254 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14255     // thse are take from connection...
14256     
14257     /**
14258      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14259      */
14260     /**
14261      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14262      * extra parameters to each request made by this object. (defaults to undefined)
14263      */
14264     /**
14265      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14266      *  to each request made by this object. (defaults to undefined)
14267      */
14268     /**
14269      * @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)
14270      */
14271     /**
14272      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14273      */
14274      /**
14275      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14276      * @type Boolean
14277      */
14278   
14279
14280     /**
14281      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14282      * @type Boolean
14283      */
14284     /**
14285      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14286      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14287      * a finer-grained basis than the DataProxy events.
14288      */
14289     getConnection : function(){
14290         return this.useAjax ? Roo.Ajax : this.conn;
14291     },
14292
14293     /**
14294      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14295      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14296      * process that block using the passed callback.
14297      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14298      * for the request to the remote server.
14299      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14300      * object into a block of Roo.data.Records.
14301      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14302      * The function must be passed <ul>
14303      * <li>The Record block object</li>
14304      * <li>The "arg" argument from the load function</li>
14305      * <li>A boolean success indicator</li>
14306      * </ul>
14307      * @param {Object} scope The scope in which to call the callback
14308      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14309      */
14310     load : function(params, reader, callback, scope, arg){
14311         if(this.fireEvent("beforeload", this, params) !== false){
14312             var  o = {
14313                 params : params || {},
14314                 request: {
14315                     callback : callback,
14316                     scope : scope,
14317                     arg : arg
14318                 },
14319                 reader: reader,
14320                 callback : this.loadResponse,
14321                 scope: this
14322             };
14323             if(this.useAjax){
14324                 Roo.applyIf(o, this.conn);
14325                 if(this.activeRequest){
14326                     Roo.Ajax.abort(this.activeRequest);
14327                 }
14328                 this.activeRequest = Roo.Ajax.request(o);
14329             }else{
14330                 this.conn.request(o);
14331             }
14332         }else{
14333             callback.call(scope||this, null, arg, false);
14334         }
14335     },
14336
14337     // private
14338     loadResponse : function(o, success, response){
14339         delete this.activeRequest;
14340         if(!success){
14341             this.fireEvent("loadexception", this, o, response);
14342             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14343             return;
14344         }
14345         var result;
14346         try {
14347             result = o.reader.read(response);
14348         }catch(e){
14349             this.fireEvent("loadexception", this, o, response, e);
14350             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14351             return;
14352         }
14353         
14354         this.fireEvent("load", this, o, o.request.arg);
14355         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14356     },
14357
14358     // private
14359     update : function(dataSet){
14360
14361     },
14362
14363     // private
14364     updateResponse : function(dataSet){
14365
14366     }
14367 });/*
14368  * Based on:
14369  * Ext JS Library 1.1.1
14370  * Copyright(c) 2006-2007, Ext JS, LLC.
14371  *
14372  * Originally Released Under LGPL - original licence link has changed is not relivant.
14373  *
14374  * Fork - LGPL
14375  * <script type="text/javascript">
14376  */
14377
14378 /**
14379  * @class Roo.data.ScriptTagProxy
14380  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14381  * other than the originating domain of the running page.<br><br>
14382  * <p>
14383  * <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
14384  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14385  * <p>
14386  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14387  * source code that is used as the source inside a &lt;script> tag.<br><br>
14388  * <p>
14389  * In order for the browser to process the returned data, the server must wrap the data object
14390  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14391  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14392  * depending on whether the callback name was passed:
14393  * <p>
14394  * <pre><code>
14395 boolean scriptTag = false;
14396 String cb = request.getParameter("callback");
14397 if (cb != null) {
14398     scriptTag = true;
14399     response.setContentType("text/javascript");
14400 } else {
14401     response.setContentType("application/x-json");
14402 }
14403 Writer out = response.getWriter();
14404 if (scriptTag) {
14405     out.write(cb + "(");
14406 }
14407 out.print(dataBlock.toJsonString());
14408 if (scriptTag) {
14409     out.write(");");
14410 }
14411 </pre></code>
14412  *
14413  * @constructor
14414  * @param {Object} config A configuration object.
14415  */
14416 Roo.data.ScriptTagProxy = function(config){
14417     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14418     Roo.apply(this, config);
14419     this.head = document.getElementsByTagName("head")[0];
14420 };
14421
14422 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14423
14424 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14425     /**
14426      * @cfg {String} url The URL from which to request the data object.
14427      */
14428     /**
14429      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14430      */
14431     timeout : 30000,
14432     /**
14433      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14434      * the server the name of the callback function set up by the load call to process the returned data object.
14435      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14436      * javascript output which calls this named function passing the data object as its only parameter.
14437      */
14438     callbackParam : "callback",
14439     /**
14440      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14441      * name to the request.
14442      */
14443     nocache : true,
14444
14445     /**
14446      * Load data from the configured URL, read the data object into
14447      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14448      * process that block using the passed callback.
14449      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14450      * for the request to the remote server.
14451      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14452      * object into a block of Roo.data.Records.
14453      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14454      * The function must be passed <ul>
14455      * <li>The Record block object</li>
14456      * <li>The "arg" argument from the load function</li>
14457      * <li>A boolean success indicator</li>
14458      * </ul>
14459      * @param {Object} scope The scope in which to call the callback
14460      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14461      */
14462     load : function(params, reader, callback, scope, arg){
14463         if(this.fireEvent("beforeload", this, params) !== false){
14464
14465             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14466
14467             var url = this.url;
14468             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14469             if(this.nocache){
14470                 url += "&_dc=" + (new Date().getTime());
14471             }
14472             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14473             var trans = {
14474                 id : transId,
14475                 cb : "stcCallback"+transId,
14476                 scriptId : "stcScript"+transId,
14477                 params : params,
14478                 arg : arg,
14479                 url : url,
14480                 callback : callback,
14481                 scope : scope,
14482                 reader : reader
14483             };
14484             var conn = this;
14485
14486             window[trans.cb] = function(o){
14487                 conn.handleResponse(o, trans);
14488             };
14489
14490             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14491
14492             if(this.autoAbort !== false){
14493                 this.abort();
14494             }
14495
14496             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14497
14498             var script = document.createElement("script");
14499             script.setAttribute("src", url);
14500             script.setAttribute("type", "text/javascript");
14501             script.setAttribute("id", trans.scriptId);
14502             this.head.appendChild(script);
14503
14504             this.trans = trans;
14505         }else{
14506             callback.call(scope||this, null, arg, false);
14507         }
14508     },
14509
14510     // private
14511     isLoading : function(){
14512         return this.trans ? true : false;
14513     },
14514
14515     /**
14516      * Abort the current server request.
14517      */
14518     abort : function(){
14519         if(this.isLoading()){
14520             this.destroyTrans(this.trans);
14521         }
14522     },
14523
14524     // private
14525     destroyTrans : function(trans, isLoaded){
14526         this.head.removeChild(document.getElementById(trans.scriptId));
14527         clearTimeout(trans.timeoutId);
14528         if(isLoaded){
14529             window[trans.cb] = undefined;
14530             try{
14531                 delete window[trans.cb];
14532             }catch(e){}
14533         }else{
14534             // if hasn't been loaded, wait for load to remove it to prevent script error
14535             window[trans.cb] = function(){
14536                 window[trans.cb] = undefined;
14537                 try{
14538                     delete window[trans.cb];
14539                 }catch(e){}
14540             };
14541         }
14542     },
14543
14544     // private
14545     handleResponse : function(o, trans){
14546         this.trans = false;
14547         this.destroyTrans(trans, true);
14548         var result;
14549         try {
14550             result = trans.reader.readRecords(o);
14551         }catch(e){
14552             this.fireEvent("loadexception", this, o, trans.arg, e);
14553             trans.callback.call(trans.scope||window, null, trans.arg, false);
14554             return;
14555         }
14556         this.fireEvent("load", this, o, trans.arg);
14557         trans.callback.call(trans.scope||window, result, trans.arg, true);
14558     },
14559
14560     // private
14561     handleFailure : function(trans){
14562         this.trans = false;
14563         this.destroyTrans(trans, false);
14564         this.fireEvent("loadexception", this, null, trans.arg);
14565         trans.callback.call(trans.scope||window, null, trans.arg, false);
14566     }
14567 });/*
14568  * Based on:
14569  * Ext JS Library 1.1.1
14570  * Copyright(c) 2006-2007, Ext JS, LLC.
14571  *
14572  * Originally Released Under LGPL - original licence link has changed is not relivant.
14573  *
14574  * Fork - LGPL
14575  * <script type="text/javascript">
14576  */
14577
14578 /**
14579  * @class Roo.data.JsonReader
14580  * @extends Roo.data.DataReader
14581  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14582  * based on mappings in a provided Roo.data.Record constructor.
14583  * 
14584  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14585  * in the reply previously. 
14586  * 
14587  * <p>
14588  * Example code:
14589  * <pre><code>
14590 var RecordDef = Roo.data.Record.create([
14591     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14592     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14593 ]);
14594 var myReader = new Roo.data.JsonReader({
14595     totalProperty: "results",    // The property which contains the total dataset size (optional)
14596     root: "rows",                // The property which contains an Array of row objects
14597     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14598 }, RecordDef);
14599 </code></pre>
14600  * <p>
14601  * This would consume a JSON file like this:
14602  * <pre><code>
14603 { 'results': 2, 'rows': [
14604     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14605     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14606 }
14607 </code></pre>
14608  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14609  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14610  * paged from the remote server.
14611  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14612  * @cfg {String} root name of the property which contains the Array of row objects.
14613  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14614  * @cfg {Array} fields Array of field definition objects
14615  * @constructor
14616  * Create a new JsonReader
14617  * @param {Object} meta Metadata configuration options
14618  * @param {Object} recordType Either an Array of field definition objects,
14619  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14620  */
14621 Roo.data.JsonReader = function(meta, recordType){
14622     
14623     meta = meta || {};
14624     // set some defaults:
14625     Roo.applyIf(meta, {
14626         totalProperty: 'total',
14627         successProperty : 'success',
14628         root : 'data',
14629         id : 'id'
14630     });
14631     
14632     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14633 };
14634 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14635     
14636     readerType : 'Json',
14637     
14638     /**
14639      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14640      * Used by Store query builder to append _requestMeta to params.
14641      * 
14642      */
14643     metaFromRemote : false,
14644     /**
14645      * This method is only used by a DataProxy which has retrieved data from a remote server.
14646      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14647      * @return {Object} data A data block which is used by an Roo.data.Store object as
14648      * a cache of Roo.data.Records.
14649      */
14650     read : function(response){
14651         var json = response.responseText;
14652        
14653         var o = /* eval:var:o */ eval("("+json+")");
14654         if(!o) {
14655             throw {message: "JsonReader.read: Json object not found"};
14656         }
14657         
14658         if(o.metaData){
14659             
14660             delete this.ef;
14661             this.metaFromRemote = true;
14662             this.meta = o.metaData;
14663             this.recordType = Roo.data.Record.create(o.metaData.fields);
14664             this.onMetaChange(this.meta, this.recordType, o);
14665         }
14666         return this.readRecords(o);
14667     },
14668
14669     // private function a store will implement
14670     onMetaChange : function(meta, recordType, o){
14671
14672     },
14673
14674     /**
14675          * @ignore
14676          */
14677     simpleAccess: function(obj, subsc) {
14678         return obj[subsc];
14679     },
14680
14681         /**
14682          * @ignore
14683          */
14684     getJsonAccessor: function(){
14685         var re = /[\[\.]/;
14686         return function(expr) {
14687             try {
14688                 return(re.test(expr))
14689                     ? new Function("obj", "return obj." + expr)
14690                     : function(obj){
14691                         return obj[expr];
14692                     };
14693             } catch(e){}
14694             return Roo.emptyFn;
14695         };
14696     }(),
14697
14698     /**
14699      * Create a data block containing Roo.data.Records from an XML document.
14700      * @param {Object} o An object which contains an Array of row objects in the property specified
14701      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14702      * which contains the total size of the dataset.
14703      * @return {Object} data A data block which is used by an Roo.data.Store object as
14704      * a cache of Roo.data.Records.
14705      */
14706     readRecords : function(o){
14707         /**
14708          * After any data loads, the raw JSON data is available for further custom processing.
14709          * @type Object
14710          */
14711         this.o = o;
14712         var s = this.meta, Record = this.recordType,
14713             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14714
14715 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14716         if (!this.ef) {
14717             if(s.totalProperty) {
14718                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14719                 }
14720                 if(s.successProperty) {
14721                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14722                 }
14723                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14724                 if (s.id) {
14725                         var g = this.getJsonAccessor(s.id);
14726                         this.getId = function(rec) {
14727                                 var r = g(rec);  
14728                                 return (r === undefined || r === "") ? null : r;
14729                         };
14730                 } else {
14731                         this.getId = function(){return null;};
14732                 }
14733             this.ef = [];
14734             for(var jj = 0; jj < fl; jj++){
14735                 f = fi[jj];
14736                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14737                 this.ef[jj] = this.getJsonAccessor(map);
14738             }
14739         }
14740
14741         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14742         if(s.totalProperty){
14743             var vt = parseInt(this.getTotal(o), 10);
14744             if(!isNaN(vt)){
14745                 totalRecords = vt;
14746             }
14747         }
14748         if(s.successProperty){
14749             var vs = this.getSuccess(o);
14750             if(vs === false || vs === 'false'){
14751                 success = false;
14752             }
14753         }
14754         var records = [];
14755         for(var i = 0; i < c; i++){
14756                 var n = root[i];
14757             var values = {};
14758             var id = this.getId(n);
14759             for(var j = 0; j < fl; j++){
14760                 f = fi[j];
14761             var v = this.ef[j](n);
14762             if (!f.convert) {
14763                 Roo.log('missing convert for ' + f.name);
14764                 Roo.log(f);
14765                 continue;
14766             }
14767             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14768             }
14769             var record = new Record(values, id);
14770             record.json = n;
14771             records[i] = record;
14772         }
14773         return {
14774             raw : o,
14775             success : success,
14776             records : records,
14777             totalRecords : totalRecords
14778         };
14779     },
14780     // used when loading children.. @see loadDataFromChildren
14781     toLoadData: function(rec)
14782     {
14783         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14784         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14785         return { data : data, total : data.length };
14786         
14787     }
14788 });/*
14789  * Based on:
14790  * Ext JS Library 1.1.1
14791  * Copyright(c) 2006-2007, Ext JS, LLC.
14792  *
14793  * Originally Released Under LGPL - original licence link has changed is not relivant.
14794  *
14795  * Fork - LGPL
14796  * <script type="text/javascript">
14797  */
14798
14799 /**
14800  * @class Roo.data.ArrayReader
14801  * @extends Roo.data.DataReader
14802  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14803  * Each element of that Array represents a row of data fields. The
14804  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14805  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14806  * <p>
14807  * Example code:.
14808  * <pre><code>
14809 var RecordDef = Roo.data.Record.create([
14810     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14811     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14812 ]);
14813 var myReader = new Roo.data.ArrayReader({
14814     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14815 }, RecordDef);
14816 </code></pre>
14817  * <p>
14818  * This would consume an Array like this:
14819  * <pre><code>
14820 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14821   </code></pre>
14822  
14823  * @constructor
14824  * Create a new JsonReader
14825  * @param {Object} meta Metadata configuration options.
14826  * @param {Object|Array} recordType Either an Array of field definition objects
14827  * 
14828  * @cfg {Array} fields Array of field definition objects
14829  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14830  * as specified to {@link Roo.data.Record#create},
14831  * or an {@link Roo.data.Record} object
14832  *
14833  * 
14834  * created using {@link Roo.data.Record#create}.
14835  */
14836 Roo.data.ArrayReader = function(meta, recordType)
14837 {    
14838     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14839 };
14840
14841 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14842     
14843       /**
14844      * Create a data block containing Roo.data.Records from an XML document.
14845      * @param {Object} o An Array of row objects which represents the dataset.
14846      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14847      * a cache of Roo.data.Records.
14848      */
14849     readRecords : function(o)
14850     {
14851         var sid = this.meta ? this.meta.id : null;
14852         var recordType = this.recordType, fields = recordType.prototype.fields;
14853         var records = [];
14854         var root = o;
14855         for(var i = 0; i < root.length; i++){
14856                 var n = root[i];
14857             var values = {};
14858             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14859             for(var j = 0, jlen = fields.length; j < jlen; j++){
14860                 var f = fields.items[j];
14861                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14862                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14863                 v = f.convert(v);
14864                 values[f.name] = v;
14865             }
14866             var record = new recordType(values, id);
14867             record.json = n;
14868             records[records.length] = record;
14869         }
14870         return {
14871             records : records,
14872             totalRecords : records.length
14873         };
14874     },
14875     // used when loading children.. @see loadDataFromChildren
14876     toLoadData: function(rec)
14877     {
14878         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14879         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14880         
14881     }
14882     
14883     
14884 });/*
14885  * - LGPL
14886  * * 
14887  */
14888
14889 /**
14890  * @class Roo.bootstrap.ComboBox
14891  * @extends Roo.bootstrap.TriggerField
14892  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14893  * @cfg {Boolean} append (true|false) default false
14894  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14895  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14896  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14897  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14898  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14899  * @cfg {Boolean} animate default true
14900  * @cfg {Boolean} emptyResultText only for touch device
14901  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14902  * @cfg {String} emptyTitle default ''
14903  * @cfg {Number} width fixed with? experimental
14904  * @constructor
14905  * Create a new ComboBox.
14906  * @param {Object} config Configuration options
14907  */
14908 Roo.bootstrap.ComboBox = function(config){
14909     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14910     this.addEvents({
14911         /**
14912          * @event expand
14913          * Fires when the dropdown list is expanded
14914         * @param {Roo.bootstrap.ComboBox} combo This combo box
14915         */
14916         'expand' : true,
14917         /**
14918          * @event collapse
14919          * Fires when the dropdown list is collapsed
14920         * @param {Roo.bootstrap.ComboBox} combo This combo box
14921         */
14922         'collapse' : true,
14923         /**
14924          * @event beforeselect
14925          * Fires before a list item is selected. Return false to cancel the selection.
14926         * @param {Roo.bootstrap.ComboBox} combo This combo box
14927         * @param {Roo.data.Record} record The data record returned from the underlying store
14928         * @param {Number} index The index of the selected item in the dropdown list
14929         */
14930         'beforeselect' : true,
14931         /**
14932          * @event select
14933          * Fires when a list item is selected
14934         * @param {Roo.bootstrap.ComboBox} combo This combo box
14935         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14936         * @param {Number} index The index of the selected item in the dropdown list
14937         */
14938         'select' : true,
14939         /**
14940          * @event beforequery
14941          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14942          * The event object passed has these properties:
14943         * @param {Roo.bootstrap.ComboBox} combo This combo box
14944         * @param {String} query The query
14945         * @param {Boolean} forceAll true to force "all" query
14946         * @param {Boolean} cancel true to cancel the query
14947         * @param {Object} e The query event object
14948         */
14949         'beforequery': true,
14950          /**
14951          * @event add
14952          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14953         * @param {Roo.bootstrap.ComboBox} combo This combo box
14954         */
14955         'add' : true,
14956         /**
14957          * @event edit
14958          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14959         * @param {Roo.bootstrap.ComboBox} combo This combo box
14960         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14961         */
14962         'edit' : true,
14963         /**
14964          * @event remove
14965          * Fires when the remove value from the combobox array
14966         * @param {Roo.bootstrap.ComboBox} combo This combo box
14967         */
14968         'remove' : true,
14969         /**
14970          * @event afterremove
14971          * Fires when the remove value from the combobox array
14972         * @param {Roo.bootstrap.ComboBox} combo This combo box
14973         */
14974         'afterremove' : true,
14975         /**
14976          * @event specialfilter
14977          * Fires when specialfilter
14978             * @param {Roo.bootstrap.ComboBox} combo This combo box
14979             */
14980         'specialfilter' : true,
14981         /**
14982          * @event tick
14983          * Fires when tick the element
14984             * @param {Roo.bootstrap.ComboBox} combo This combo box
14985             */
14986         'tick' : true,
14987         /**
14988          * @event touchviewdisplay
14989          * Fires when touch view require special display (default is using displayField)
14990             * @param {Roo.bootstrap.ComboBox} combo This combo box
14991             * @param {Object} cfg set html .
14992             */
14993         'touchviewdisplay' : true
14994         
14995     });
14996     
14997     this.item = [];
14998     this.tickItems = [];
14999     
15000     this.selectedIndex = -1;
15001     if(this.mode == 'local'){
15002         if(config.queryDelay === undefined){
15003             this.queryDelay = 10;
15004         }
15005         if(config.minChars === undefined){
15006             this.minChars = 0;
15007         }
15008     }
15009 };
15010
15011 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15012      
15013     /**
15014      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15015      * rendering into an Roo.Editor, defaults to false)
15016      */
15017     /**
15018      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15019      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15020      */
15021     /**
15022      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15023      */
15024     /**
15025      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15026      * the dropdown list (defaults to undefined, with no header element)
15027      */
15028
15029      /**
15030      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15031      */
15032      
15033      /**
15034      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15035      */
15036     listWidth: undefined,
15037     /**
15038      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15039      * mode = 'remote' or 'text' if mode = 'local')
15040      */
15041     displayField: undefined,
15042     
15043     /**
15044      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15045      * mode = 'remote' or 'value' if mode = 'local'). 
15046      * Note: use of a valueField requires the user make a selection
15047      * in order for a value to be mapped.
15048      */
15049     valueField: undefined,
15050     /**
15051      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15052      */
15053     modalTitle : '',
15054     
15055     /**
15056      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15057      * field's data value (defaults to the underlying DOM element's name)
15058      */
15059     hiddenName: undefined,
15060     /**
15061      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15062      */
15063     listClass: '',
15064     /**
15065      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15066      */
15067     selectedClass: 'active',
15068     
15069     /**
15070      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15071      */
15072     shadow:'sides',
15073     /**
15074      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15075      * anchor positions (defaults to 'tl-bl')
15076      */
15077     listAlign: 'tl-bl?',
15078     /**
15079      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15080      */
15081     maxHeight: 300,
15082     /**
15083      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15084      * query specified by the allQuery config option (defaults to 'query')
15085      */
15086     triggerAction: 'query',
15087     /**
15088      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15089      * (defaults to 4, does not apply if editable = false)
15090      */
15091     minChars : 4,
15092     /**
15093      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15094      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15095      */
15096     typeAhead: false,
15097     /**
15098      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15099      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15100      */
15101     queryDelay: 500,
15102     /**
15103      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15104      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15105      */
15106     pageSize: 0,
15107     /**
15108      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15109      * when editable = true (defaults to false)
15110      */
15111     selectOnFocus:false,
15112     /**
15113      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15114      */
15115     queryParam: 'query',
15116     /**
15117      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15118      * when mode = 'remote' (defaults to 'Loading...')
15119      */
15120     loadingText: 'Loading...',
15121     /**
15122      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15123      */
15124     resizable: false,
15125     /**
15126      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15127      */
15128     handleHeight : 8,
15129     /**
15130      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15131      * traditional select (defaults to true)
15132      */
15133     editable: true,
15134     /**
15135      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15136      */
15137     allQuery: '',
15138     /**
15139      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15140      */
15141     mode: 'remote',
15142     /**
15143      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15144      * listWidth has a higher value)
15145      */
15146     minListWidth : 70,
15147     /**
15148      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15149      * allow the user to set arbitrary text into the field (defaults to false)
15150      */
15151     forceSelection:false,
15152     /**
15153      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15154      * if typeAhead = true (defaults to 250)
15155      */
15156     typeAheadDelay : 250,
15157     /**
15158      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15159      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15160      */
15161     valueNotFoundText : undefined,
15162     /**
15163      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15164      */
15165     blockFocus : false,
15166     
15167     /**
15168      * @cfg {Boolean} disableClear Disable showing of clear button.
15169      */
15170     disableClear : false,
15171     /**
15172      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15173      */
15174     alwaysQuery : false,
15175     
15176     /**
15177      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15178      */
15179     multiple : false,
15180     
15181     /**
15182      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15183      */
15184     invalidClass : "has-warning",
15185     
15186     /**
15187      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15188      */
15189     validClass : "has-success",
15190     
15191     /**
15192      * @cfg {Boolean} specialFilter (true|false) special filter default false
15193      */
15194     specialFilter : false,
15195     
15196     /**
15197      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15198      */
15199     mobileTouchView : true,
15200     
15201     /**
15202      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15203      */
15204     useNativeIOS : false,
15205     
15206     /**
15207      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15208      */
15209     mobile_restrict_height : false,
15210     
15211     ios_options : false,
15212     
15213     //private
15214     addicon : false,
15215     editicon: false,
15216     
15217     page: 0,
15218     hasQuery: false,
15219     append: false,
15220     loadNext: false,
15221     autoFocus : true,
15222     tickable : false,
15223     btnPosition : 'right',
15224     triggerList : true,
15225     showToggleBtn : true,
15226     animate : true,
15227     emptyResultText: 'Empty',
15228     triggerText : 'Select',
15229     emptyTitle : '',
15230     width : false,
15231     
15232     // element that contains real text value.. (when hidden is used..)
15233     
15234     getAutoCreate : function()
15235     {   
15236         var cfg = false;
15237         //render
15238         /*
15239          * Render classic select for iso
15240          */
15241         
15242         if(Roo.isIOS && this.useNativeIOS){
15243             cfg = this.getAutoCreateNativeIOS();
15244             return cfg;
15245         }
15246         
15247         /*
15248          * Touch Devices
15249          */
15250         
15251         if(Roo.isTouch && this.mobileTouchView){
15252             cfg = this.getAutoCreateTouchView();
15253             return cfg;;
15254         }
15255         
15256         /*
15257          *  Normal ComboBox
15258          */
15259         if(!this.tickable){
15260             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15261             return cfg;
15262         }
15263         
15264         /*
15265          *  ComboBox with tickable selections
15266          */
15267              
15268         var align = this.labelAlign || this.parentLabelAlign();
15269         
15270         cfg = {
15271             cls : 'form-group roo-combobox-tickable' //input-group
15272         };
15273         
15274         var btn_text_select = '';
15275         var btn_text_done = '';
15276         var btn_text_cancel = '';
15277         
15278         if (this.btn_text_show) {
15279             btn_text_select = 'Select';
15280             btn_text_done = 'Done';
15281             btn_text_cancel = 'Cancel'; 
15282         }
15283         
15284         var buttons = {
15285             tag : 'div',
15286             cls : 'tickable-buttons',
15287             cn : [
15288                 {
15289                     tag : 'button',
15290                     type : 'button',
15291                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15292                     //html : this.triggerText
15293                     html: btn_text_select
15294                 },
15295                 {
15296                     tag : 'button',
15297                     type : 'button',
15298                     name : 'ok',
15299                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15300                     //html : 'Done'
15301                     html: btn_text_done
15302                 },
15303                 {
15304                     tag : 'button',
15305                     type : 'button',
15306                     name : 'cancel',
15307                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15308                     //html : 'Cancel'
15309                     html: btn_text_cancel
15310                 }
15311             ]
15312         };
15313         
15314         if(this.editable){
15315             buttons.cn.unshift({
15316                 tag: 'input',
15317                 cls: 'roo-select2-search-field-input'
15318             });
15319         }
15320         
15321         var _this = this;
15322         
15323         Roo.each(buttons.cn, function(c){
15324             if (_this.size) {
15325                 c.cls += ' btn-' + _this.size;
15326             }
15327
15328             if (_this.disabled) {
15329                 c.disabled = true;
15330             }
15331         });
15332         
15333         var box = {
15334             tag: 'div',
15335             style : 'display: contents',
15336             cn: [
15337                 {
15338                     tag: 'input',
15339                     type : 'hidden',
15340                     cls: 'form-hidden-field'
15341                 },
15342                 {
15343                     tag: 'ul',
15344                     cls: 'roo-select2-choices',
15345                     cn:[
15346                         {
15347                             tag: 'li',
15348                             cls: 'roo-select2-search-field',
15349                             cn: [
15350                                 buttons
15351                             ]
15352                         }
15353                     ]
15354                 }
15355             ]
15356         };
15357         
15358         var combobox = {
15359             cls: 'roo-select2-container input-group roo-select2-container-multi',
15360             cn: [
15361                 
15362                 box
15363 //                {
15364 //                    tag: 'ul',
15365 //                    cls: 'typeahead typeahead-long dropdown-menu',
15366 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15367 //                }
15368             ]
15369         };
15370         
15371         if(this.hasFeedback && !this.allowBlank){
15372             
15373             var feedback = {
15374                 tag: 'span',
15375                 cls: 'glyphicon form-control-feedback'
15376             };
15377
15378             combobox.cn.push(feedback);
15379         }
15380         
15381         
15382         
15383         var indicator = {
15384             tag : 'i',
15385             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15386             tooltip : 'This field is required'
15387         };
15388         if (Roo.bootstrap.version == 4) {
15389             indicator = {
15390                 tag : 'i',
15391                 style : 'display:none'
15392             };
15393         }
15394         if (align ==='left' && this.fieldLabel.length) {
15395             
15396             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15397             
15398             cfg.cn = [
15399                 indicator,
15400                 {
15401                     tag: 'label',
15402                     'for' :  id,
15403                     cls : 'control-label col-form-label',
15404                     html : this.fieldLabel
15405
15406                 },
15407                 {
15408                     cls : "", 
15409                     cn: [
15410                         combobox
15411                     ]
15412                 }
15413
15414             ];
15415             
15416             var labelCfg = cfg.cn[1];
15417             var contentCfg = cfg.cn[2];
15418             
15419
15420             if(this.indicatorpos == 'right'){
15421                 
15422                 cfg.cn = [
15423                     {
15424                         tag: 'label',
15425                         'for' :  id,
15426                         cls : 'control-label col-form-label',
15427                         cn : [
15428                             {
15429                                 tag : 'span',
15430                                 html : this.fieldLabel
15431                             },
15432                             indicator
15433                         ]
15434                     },
15435                     {
15436                         cls : "",
15437                         cn: [
15438                             combobox
15439                         ]
15440                     }
15441
15442                 ];
15443                 
15444                 
15445                 
15446                 labelCfg = cfg.cn[0];
15447                 contentCfg = cfg.cn[1];
15448             
15449             }
15450             
15451             if(this.labelWidth > 12){
15452                 labelCfg.style = "width: " + this.labelWidth + 'px';
15453             }
15454             if(this.width * 1 > 0){
15455                 contentCfg.style = "width: " + this.width + 'px';
15456             }
15457             if(this.labelWidth < 13 && this.labelmd == 0){
15458                 this.labelmd = this.labelWidth;
15459             }
15460             
15461             if(this.labellg > 0){
15462                 labelCfg.cls += ' col-lg-' + this.labellg;
15463                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15464             }
15465             
15466             if(this.labelmd > 0){
15467                 labelCfg.cls += ' col-md-' + this.labelmd;
15468                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15469             }
15470             
15471             if(this.labelsm > 0){
15472                 labelCfg.cls += ' col-sm-' + this.labelsm;
15473                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15474             }
15475             
15476             if(this.labelxs > 0){
15477                 labelCfg.cls += ' col-xs-' + this.labelxs;
15478                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15479             }
15480                 
15481                 
15482         } else if ( this.fieldLabel.length) {
15483 //                Roo.log(" label");
15484                  cfg.cn = [
15485                    indicator,
15486                     {
15487                         tag: 'label',
15488                         //cls : 'input-group-addon',
15489                         html : this.fieldLabel
15490                     },
15491                     combobox
15492                 ];
15493                 
15494                 if(this.indicatorpos == 'right'){
15495                     cfg.cn = [
15496                         {
15497                             tag: 'label',
15498                             //cls : 'input-group-addon',
15499                             html : this.fieldLabel
15500                         },
15501                         indicator,
15502                         combobox
15503                     ];
15504                     
15505                 }
15506
15507         } else {
15508             
15509 //                Roo.log(" no label && no align");
15510                 cfg = combobox
15511                      
15512                 
15513         }
15514          
15515         var settings=this;
15516         ['xs','sm','md','lg'].map(function(size){
15517             if (settings[size]) {
15518                 cfg.cls += ' col-' + size + '-' + settings[size];
15519             }
15520         });
15521         
15522         return cfg;
15523         
15524     },
15525     
15526     _initEventsCalled : false,
15527     
15528     // private
15529     initEvents: function()
15530     {   
15531         if (this._initEventsCalled) { // as we call render... prevent looping...
15532             return;
15533         }
15534         this._initEventsCalled = true;
15535         
15536         if (!this.store) {
15537             throw "can not find store for combo";
15538         }
15539         
15540         this.indicator = this.indicatorEl();
15541         
15542         this.store = Roo.factory(this.store, Roo.data);
15543         this.store.parent = this;
15544         
15545         // if we are building from html. then this element is so complex, that we can not really
15546         // use the rendered HTML.
15547         // so we have to trash and replace the previous code.
15548         if (Roo.XComponent.build_from_html) {
15549             // remove this element....
15550             var e = this.el.dom, k=0;
15551             while (e ) { e = e.previousSibling;  ++k;}
15552
15553             this.el.remove();
15554             
15555             this.el=false;
15556             this.rendered = false;
15557             
15558             this.render(this.parent().getChildContainer(true), k);
15559         }
15560         
15561         if(Roo.isIOS && this.useNativeIOS){
15562             this.initIOSView();
15563             return;
15564         }
15565         
15566         /*
15567          * Touch Devices
15568          */
15569         
15570         if(Roo.isTouch && this.mobileTouchView){
15571             this.initTouchView();
15572             return;
15573         }
15574         
15575         if(this.tickable){
15576             this.initTickableEvents();
15577             return;
15578         }
15579         
15580         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15581         
15582         if(this.hiddenName){
15583             
15584             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15585             
15586             this.hiddenField.dom.value =
15587                 this.hiddenValue !== undefined ? this.hiddenValue :
15588                 this.value !== undefined ? this.value : '';
15589
15590             // prevent input submission
15591             this.el.dom.removeAttribute('name');
15592             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15593              
15594              
15595         }
15596         //if(Roo.isGecko){
15597         //    this.el.dom.setAttribute('autocomplete', 'off');
15598         //}
15599         
15600         var cls = 'x-combo-list';
15601         
15602         //this.list = new Roo.Layer({
15603         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15604         //});
15605         
15606         var _this = this;
15607         
15608         (function(){
15609             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15610             _this.list.setWidth(lw);
15611         }).defer(100);
15612         
15613         this.list.on('mouseover', this.onViewOver, this);
15614         this.list.on('mousemove', this.onViewMove, this);
15615         this.list.on('scroll', this.onViewScroll, this);
15616         
15617         /*
15618         this.list.swallowEvent('mousewheel');
15619         this.assetHeight = 0;
15620
15621         if(this.title){
15622             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15623             this.assetHeight += this.header.getHeight();
15624         }
15625
15626         this.innerList = this.list.createChild({cls:cls+'-inner'});
15627         this.innerList.on('mouseover', this.onViewOver, this);
15628         this.innerList.on('mousemove', this.onViewMove, this);
15629         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15630         
15631         if(this.allowBlank && !this.pageSize && !this.disableClear){
15632             this.footer = this.list.createChild({cls:cls+'-ft'});
15633             this.pageTb = new Roo.Toolbar(this.footer);
15634            
15635         }
15636         if(this.pageSize){
15637             this.footer = this.list.createChild({cls:cls+'-ft'});
15638             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15639                     {pageSize: this.pageSize});
15640             
15641         }
15642         
15643         if (this.pageTb && this.allowBlank && !this.disableClear) {
15644             var _this = this;
15645             this.pageTb.add(new Roo.Toolbar.Fill(), {
15646                 cls: 'x-btn-icon x-btn-clear',
15647                 text: '&#160;',
15648                 handler: function()
15649                 {
15650                     _this.collapse();
15651                     _this.clearValue();
15652                     _this.onSelect(false, -1);
15653                 }
15654             });
15655         }
15656         if (this.footer) {
15657             this.assetHeight += this.footer.getHeight();
15658         }
15659         */
15660             
15661         if(!this.tpl){
15662             this.tpl = Roo.bootstrap.version == 4 ?
15663                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15664                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15665         }
15666
15667         this.view = new Roo.View(this.list, this.tpl, {
15668             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15669         });
15670         //this.view.wrapEl.setDisplayed(false);
15671         this.view.on('click', this.onViewClick, this);
15672         
15673         
15674         this.store.on('beforeload', this.onBeforeLoad, this);
15675         this.store.on('load', this.onLoad, this);
15676         this.store.on('loadexception', this.onLoadException, this);
15677         /*
15678         if(this.resizable){
15679             this.resizer = new Roo.Resizable(this.list,  {
15680                pinned:true, handles:'se'
15681             });
15682             this.resizer.on('resize', function(r, w, h){
15683                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15684                 this.listWidth = w;
15685                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15686                 this.restrictHeight();
15687             }, this);
15688             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15689         }
15690         */
15691         if(!this.editable){
15692             this.editable = true;
15693             this.setEditable(false);
15694         }
15695         
15696         /*
15697         
15698         if (typeof(this.events.add.listeners) != 'undefined') {
15699             
15700             this.addicon = this.wrap.createChild(
15701                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15702        
15703             this.addicon.on('click', function(e) {
15704                 this.fireEvent('add', this);
15705             }, this);
15706         }
15707         if (typeof(this.events.edit.listeners) != 'undefined') {
15708             
15709             this.editicon = this.wrap.createChild(
15710                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15711             if (this.addicon) {
15712                 this.editicon.setStyle('margin-left', '40px');
15713             }
15714             this.editicon.on('click', function(e) {
15715                 
15716                 // we fire even  if inothing is selected..
15717                 this.fireEvent('edit', this, this.lastData );
15718                 
15719             }, this);
15720         }
15721         */
15722         
15723         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15724             "up" : function(e){
15725                 this.inKeyMode = true;
15726                 this.selectPrev();
15727             },
15728
15729             "down" : function(e){
15730                 if(!this.isExpanded()){
15731                     this.onTriggerClick();
15732                 }else{
15733                     this.inKeyMode = true;
15734                     this.selectNext();
15735                 }
15736             },
15737
15738             "enter" : function(e){
15739 //                this.onViewClick();
15740                 //return true;
15741                 this.collapse();
15742                 
15743                 if(this.fireEvent("specialkey", this, e)){
15744                     this.onViewClick(false);
15745                 }
15746                 
15747                 return true;
15748             },
15749
15750             "esc" : function(e){
15751                 this.collapse();
15752             },
15753
15754             "tab" : function(e){
15755                 this.collapse();
15756                 
15757                 if(this.fireEvent("specialkey", this, e)){
15758                     this.onViewClick(false);
15759                 }
15760                 
15761                 return true;
15762             },
15763
15764             scope : this,
15765
15766             doRelay : function(foo, bar, hname){
15767                 if(hname == 'down' || this.scope.isExpanded()){
15768                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15769                 }
15770                 return true;
15771             },
15772
15773             forceKeyDown: true
15774         });
15775         
15776         
15777         this.queryDelay = Math.max(this.queryDelay || 10,
15778                 this.mode == 'local' ? 10 : 250);
15779         
15780         
15781         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15782         
15783         if(this.typeAhead){
15784             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15785         }
15786         if(this.editable !== false){
15787             this.inputEl().on("keyup", this.onKeyUp, this);
15788         }
15789         if(this.forceSelection){
15790             this.inputEl().on('blur', this.doForce, this);
15791         }
15792         
15793         if(this.multiple){
15794             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15795             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15796         }
15797     },
15798     
15799     initTickableEvents: function()
15800     {   
15801         this.createList();
15802         
15803         if(this.hiddenName){
15804             
15805             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15806             
15807             this.hiddenField.dom.value =
15808                 this.hiddenValue !== undefined ? this.hiddenValue :
15809                 this.value !== undefined ? this.value : '';
15810
15811             // prevent input submission
15812             this.el.dom.removeAttribute('name');
15813             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15814              
15815              
15816         }
15817         
15818 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15819         
15820         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15821         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15822         if(this.triggerList){
15823             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15824         }
15825          
15826         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15827         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15828         
15829         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15830         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15831         
15832         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15833         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15834         
15835         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15836         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15837         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15838         
15839         this.okBtn.hide();
15840         this.cancelBtn.hide();
15841         
15842         var _this = this;
15843         
15844         (function(){
15845             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15846             _this.list.setWidth(lw);
15847         }).defer(100);
15848         
15849         this.list.on('mouseover', this.onViewOver, this);
15850         this.list.on('mousemove', this.onViewMove, this);
15851         
15852         this.list.on('scroll', this.onViewScroll, this);
15853         
15854         if(!this.tpl){
15855             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15856                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15857         }
15858
15859         this.view = new Roo.View(this.list, this.tpl, {
15860             singleSelect:true,
15861             tickable:true,
15862             parent:this,
15863             store: this.store,
15864             selectedClass: this.selectedClass
15865         });
15866         
15867         //this.view.wrapEl.setDisplayed(false);
15868         this.view.on('click', this.onViewClick, this);
15869         
15870         
15871         
15872         this.store.on('beforeload', this.onBeforeLoad, this);
15873         this.store.on('load', this.onLoad, this);
15874         this.store.on('loadexception', this.onLoadException, this);
15875         
15876         if(this.editable){
15877             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15878                 "up" : function(e){
15879                     this.inKeyMode = true;
15880                     this.selectPrev();
15881                 },
15882
15883                 "down" : function(e){
15884                     this.inKeyMode = true;
15885                     this.selectNext();
15886                 },
15887
15888                 "enter" : function(e){
15889                     if(this.fireEvent("specialkey", this, e)){
15890                         this.onViewClick(false);
15891                     }
15892                     
15893                     return true;
15894                 },
15895
15896                 "esc" : function(e){
15897                     this.onTickableFooterButtonClick(e, false, false);
15898                 },
15899
15900                 "tab" : function(e){
15901                     this.fireEvent("specialkey", this, e);
15902                     
15903                     this.onTickableFooterButtonClick(e, false, false);
15904                     
15905                     return true;
15906                 },
15907
15908                 scope : this,
15909
15910                 doRelay : function(e, fn, key){
15911                     if(this.scope.isExpanded()){
15912                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15913                     }
15914                     return true;
15915                 },
15916
15917                 forceKeyDown: true
15918             });
15919         }
15920         
15921         this.queryDelay = Math.max(this.queryDelay || 10,
15922                 this.mode == 'local' ? 10 : 250);
15923         
15924         
15925         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15926         
15927         if(this.typeAhead){
15928             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15929         }
15930         
15931         if(this.editable !== false){
15932             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15933         }
15934         
15935         this.indicator = this.indicatorEl();
15936         
15937         if(this.indicator){
15938             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15939             this.indicator.hide();
15940         }
15941         
15942     },
15943
15944     onDestroy : function(){
15945         if(this.view){
15946             this.view.setStore(null);
15947             this.view.el.removeAllListeners();
15948             this.view.el.remove();
15949             this.view.purgeListeners();
15950         }
15951         if(this.list){
15952             this.list.dom.innerHTML  = '';
15953         }
15954         
15955         if(this.store){
15956             this.store.un('beforeload', this.onBeforeLoad, this);
15957             this.store.un('load', this.onLoad, this);
15958             this.store.un('loadexception', this.onLoadException, this);
15959         }
15960         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15961     },
15962
15963     // private
15964     fireKey : function(e){
15965         if(e.isNavKeyPress() && !this.list.isVisible()){
15966             this.fireEvent("specialkey", this, e);
15967         }
15968     },
15969
15970     // private
15971     onResize: function(w, h)
15972     {
15973         
15974         
15975 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15976 //        
15977 //        if(typeof w != 'number'){
15978 //            // we do not handle it!?!?
15979 //            return;
15980 //        }
15981 //        var tw = this.trigger.getWidth();
15982 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15983 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15984 //        var x = w - tw;
15985 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15986 //            
15987 //        //this.trigger.setStyle('left', x+'px');
15988 //        
15989 //        if(this.list && this.listWidth === undefined){
15990 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15991 //            this.list.setWidth(lw);
15992 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15993 //        }
15994         
15995     
15996         
15997     },
15998
15999     /**
16000      * Allow or prevent the user from directly editing the field text.  If false is passed,
16001      * the user will only be able to select from the items defined in the dropdown list.  This method
16002      * is the runtime equivalent of setting the 'editable' config option at config time.
16003      * @param {Boolean} value True to allow the user to directly edit the field text
16004      */
16005     setEditable : function(value){
16006         if(value == this.editable){
16007             return;
16008         }
16009         this.editable = value;
16010         if(!value){
16011             this.inputEl().dom.setAttribute('readOnly', true);
16012             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16013             this.inputEl().addClass('x-combo-noedit');
16014         }else{
16015             this.inputEl().dom.setAttribute('readOnly', false);
16016             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16017             this.inputEl().removeClass('x-combo-noedit');
16018         }
16019     },
16020
16021     // private
16022     
16023     onBeforeLoad : function(combo,opts){
16024         if(!this.hasFocus){
16025             return;
16026         }
16027          if (!opts.add) {
16028             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16029          }
16030         this.restrictHeight();
16031         this.selectedIndex = -1;
16032     },
16033
16034     // private
16035     onLoad : function(){
16036         
16037         this.hasQuery = false;
16038         
16039         if(!this.hasFocus){
16040             return;
16041         }
16042         
16043         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16044             this.loading.hide();
16045         }
16046         
16047         if(this.store.getCount() > 0){
16048             
16049             this.expand();
16050             this.restrictHeight();
16051             if(this.lastQuery == this.allQuery){
16052                 if(this.editable && !this.tickable){
16053                     this.inputEl().dom.select();
16054                 }
16055                 
16056                 if(
16057                     !this.selectByValue(this.value, true) &&
16058                     this.autoFocus && 
16059                     (
16060                         !this.store.lastOptions ||
16061                         typeof(this.store.lastOptions.add) == 'undefined' || 
16062                         this.store.lastOptions.add != true
16063                     )
16064                 ){
16065                     this.select(0, true);
16066                 }
16067             }else{
16068                 if(this.autoFocus){
16069                     this.selectNext();
16070                 }
16071                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16072                     this.taTask.delay(this.typeAheadDelay);
16073                 }
16074             }
16075         }else{
16076             this.onEmptyResults();
16077         }
16078         
16079         //this.el.focus();
16080     },
16081     // private
16082     onLoadException : function()
16083     {
16084         this.hasQuery = false;
16085         
16086         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16087             this.loading.hide();
16088         }
16089         
16090         if(this.tickable && this.editable){
16091             return;
16092         }
16093         
16094         this.collapse();
16095         // only causes errors at present
16096         //Roo.log(this.store.reader.jsonData);
16097         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16098             // fixme
16099             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16100         //}
16101         
16102         
16103     },
16104     // private
16105     onTypeAhead : function(){
16106         if(this.store.getCount() > 0){
16107             var r = this.store.getAt(0);
16108             var newValue = r.data[this.displayField];
16109             var len = newValue.length;
16110             var selStart = this.getRawValue().length;
16111             
16112             if(selStart != len){
16113                 this.setRawValue(newValue);
16114                 this.selectText(selStart, newValue.length);
16115             }
16116         }
16117     },
16118
16119     // private
16120     onSelect : function(record, index){
16121         
16122         if(this.fireEvent('beforeselect', this, record, index) !== false){
16123         
16124             this.setFromData(index > -1 ? record.data : false);
16125             
16126             this.collapse();
16127             this.fireEvent('select', this, record, index);
16128         }
16129     },
16130
16131     /**
16132      * Returns the currently selected field value or empty string if no value is set.
16133      * @return {String} value The selected value
16134      */
16135     getValue : function()
16136     {
16137         if(Roo.isIOS && this.useNativeIOS){
16138             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16139         }
16140         
16141         if(this.multiple){
16142             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16143         }
16144         
16145         if(this.valueField){
16146             return typeof this.value != 'undefined' ? this.value : '';
16147         }else{
16148             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16149         }
16150     },
16151     
16152     getRawValue : function()
16153     {
16154         if(Roo.isIOS && this.useNativeIOS){
16155             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16156         }
16157         
16158         var v = this.inputEl().getValue();
16159         
16160         return v;
16161     },
16162
16163     /**
16164      * Clears any text/value currently set in the field
16165      */
16166     clearValue : function(){
16167         
16168         if(this.hiddenField){
16169             this.hiddenField.dom.value = '';
16170         }
16171         this.value = '';
16172         this.setRawValue('');
16173         this.lastSelectionText = '';
16174         this.lastData = false;
16175         
16176         var close = this.closeTriggerEl();
16177         
16178         if(close){
16179             close.hide();
16180         }
16181         
16182         this.validate();
16183         
16184     },
16185
16186     /**
16187      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16188      * will be displayed in the field.  If the value does not match the data value of an existing item,
16189      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16190      * Otherwise the field will be blank (although the value will still be set).
16191      * @param {String} value The value to match
16192      */
16193     setValue : function(v)
16194     {
16195         if(Roo.isIOS && this.useNativeIOS){
16196             this.setIOSValue(v);
16197             return;
16198         }
16199         
16200         if(this.multiple){
16201             this.syncValue();
16202             return;
16203         }
16204         
16205         var text = v;
16206         if(this.valueField){
16207             var r = this.findRecord(this.valueField, v);
16208             if(r){
16209                 text = r.data[this.displayField];
16210             }else if(this.valueNotFoundText !== undefined){
16211                 text = this.valueNotFoundText;
16212             }
16213         }
16214         this.lastSelectionText = text;
16215         if(this.hiddenField){
16216             this.hiddenField.dom.value = v;
16217         }
16218         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16219         this.value = v;
16220         
16221         var close = this.closeTriggerEl();
16222         
16223         if(close){
16224             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16225         }
16226         
16227         this.validate();
16228     },
16229     /**
16230      * @property {Object} the last set data for the element
16231      */
16232     
16233     lastData : false,
16234     /**
16235      * Sets the value of the field based on a object which is related to the record format for the store.
16236      * @param {Object} value the value to set as. or false on reset?
16237      */
16238     setFromData : function(o){
16239         
16240         if(this.multiple){
16241             this.addItem(o);
16242             return;
16243         }
16244             
16245         var dv = ''; // display value
16246         var vv = ''; // value value..
16247         this.lastData = o;
16248         if (this.displayField) {
16249             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16250         } else {
16251             // this is an error condition!!!
16252             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16253         }
16254         
16255         if(this.valueField){
16256             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16257         }
16258         
16259         var close = this.closeTriggerEl();
16260         
16261         if(close){
16262             if(dv.length || vv * 1 > 0){
16263                 close.show() ;
16264                 this.blockFocus=true;
16265             } else {
16266                 close.hide();
16267             }             
16268         }
16269         
16270         if(this.hiddenField){
16271             this.hiddenField.dom.value = vv;
16272             
16273             this.lastSelectionText = dv;
16274             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16275             this.value = vv;
16276             return;
16277         }
16278         // no hidden field.. - we store the value in 'value', but still display
16279         // display field!!!!
16280         this.lastSelectionText = dv;
16281         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16282         this.value = vv;
16283         
16284         
16285         
16286     },
16287     // private
16288     reset : function(){
16289         // overridden so that last data is reset..
16290         
16291         if(this.multiple){
16292             this.clearItem();
16293             return;
16294         }
16295         
16296         this.setValue(this.originalValue);
16297         //this.clearInvalid();
16298         this.lastData = false;
16299         if (this.view) {
16300             this.view.clearSelections();
16301         }
16302         
16303         this.validate();
16304     },
16305     // private
16306     findRecord : function(prop, value){
16307         var record;
16308         if(this.store.getCount() > 0){
16309             this.store.each(function(r){
16310                 if(r.data[prop] == value){
16311                     record = r;
16312                     return false;
16313                 }
16314                 return true;
16315             });
16316         }
16317         return record;
16318     },
16319     
16320     getName: function()
16321     {
16322         // returns hidden if it's set..
16323         if (!this.rendered) {return ''};
16324         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16325         
16326     },
16327     // private
16328     onViewMove : function(e, t){
16329         this.inKeyMode = false;
16330     },
16331
16332     // private
16333     onViewOver : function(e, t){
16334         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16335             return;
16336         }
16337         var item = this.view.findItemFromChild(t);
16338         
16339         if(item){
16340             var index = this.view.indexOf(item);
16341             this.select(index, false);
16342         }
16343     },
16344
16345     // private
16346     onViewClick : function(view, doFocus, el, e)
16347     {
16348         var index = this.view.getSelectedIndexes()[0];
16349         
16350         var r = this.store.getAt(index);
16351         
16352         if(this.tickable){
16353             
16354             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16355                 return;
16356             }
16357             
16358             var rm = false;
16359             var _this = this;
16360             
16361             Roo.each(this.tickItems, function(v,k){
16362                 
16363                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16364                     Roo.log(v);
16365                     _this.tickItems.splice(k, 1);
16366                     
16367                     if(typeof(e) == 'undefined' && view == false){
16368                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16369                     }
16370                     
16371                     rm = true;
16372                     return;
16373                 }
16374             });
16375             
16376             if(rm){
16377                 return;
16378             }
16379             
16380             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16381                 this.tickItems.push(r.data);
16382             }
16383             
16384             if(typeof(e) == 'undefined' && view == false){
16385                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16386             }
16387                     
16388             return;
16389         }
16390         
16391         if(r){
16392             this.onSelect(r, index);
16393         }
16394         if(doFocus !== false && !this.blockFocus){
16395             this.inputEl().focus();
16396         }
16397     },
16398
16399     // private
16400     restrictHeight : function(){
16401         //this.innerList.dom.style.height = '';
16402         //var inner = this.innerList.dom;
16403         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16404         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16405         //this.list.beginUpdate();
16406         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16407         this.list.alignTo(this.inputEl(), this.listAlign);
16408         this.list.alignTo(this.inputEl(), this.listAlign);
16409         //this.list.endUpdate();
16410     },
16411
16412     // private
16413     onEmptyResults : function(){
16414         
16415         if(this.tickable && this.editable){
16416             this.hasFocus = false;
16417             this.restrictHeight();
16418             return;
16419         }
16420         
16421         this.collapse();
16422     },
16423
16424     /**
16425      * Returns true if the dropdown list is expanded, else false.
16426      */
16427     isExpanded : function(){
16428         return this.list.isVisible();
16429     },
16430
16431     /**
16432      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16433      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16434      * @param {String} value The data value of the item to select
16435      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16436      * selected item if it is not currently in view (defaults to true)
16437      * @return {Boolean} True if the value matched an item in the list, else false
16438      */
16439     selectByValue : function(v, scrollIntoView){
16440         if(v !== undefined && v !== null){
16441             var r = this.findRecord(this.valueField || this.displayField, v);
16442             if(r){
16443                 this.select(this.store.indexOf(r), scrollIntoView);
16444                 return true;
16445             }
16446         }
16447         return false;
16448     },
16449
16450     /**
16451      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16452      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16453      * @param {Number} index The zero-based index of the list item to select
16454      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16455      * selected item if it is not currently in view (defaults to true)
16456      */
16457     select : function(index, scrollIntoView){
16458         this.selectedIndex = index;
16459         this.view.select(index);
16460         if(scrollIntoView !== false){
16461             var el = this.view.getNode(index);
16462             /*
16463              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16464              */
16465             if(el){
16466                 this.list.scrollChildIntoView(el, false);
16467             }
16468         }
16469     },
16470
16471     // private
16472     selectNext : function(){
16473         var ct = this.store.getCount();
16474         if(ct > 0){
16475             if(this.selectedIndex == -1){
16476                 this.select(0);
16477             }else if(this.selectedIndex < ct-1){
16478                 this.select(this.selectedIndex+1);
16479             }
16480         }
16481     },
16482
16483     // private
16484     selectPrev : function(){
16485         var ct = this.store.getCount();
16486         if(ct > 0){
16487             if(this.selectedIndex == -1){
16488                 this.select(0);
16489             }else if(this.selectedIndex != 0){
16490                 this.select(this.selectedIndex-1);
16491             }
16492         }
16493     },
16494
16495     // private
16496     onKeyUp : function(e){
16497         if(this.editable !== false && !e.isSpecialKey()){
16498             this.lastKey = e.getKey();
16499             this.dqTask.delay(this.queryDelay);
16500         }
16501     },
16502
16503     // private
16504     validateBlur : function(){
16505         return !this.list || !this.list.isVisible();   
16506     },
16507
16508     // private
16509     initQuery : function(){
16510         
16511         var v = this.getRawValue();
16512         
16513         if(this.tickable && this.editable){
16514             v = this.tickableInputEl().getValue();
16515         }
16516         
16517         this.doQuery(v);
16518     },
16519
16520     // private
16521     doForce : function(){
16522         if(this.inputEl().dom.value.length > 0){
16523             this.inputEl().dom.value =
16524                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16525              
16526         }
16527     },
16528
16529     /**
16530      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16531      * query allowing the query action to be canceled if needed.
16532      * @param {String} query The SQL query to execute
16533      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16534      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16535      * saved in the current store (defaults to false)
16536      */
16537     doQuery : function(q, forceAll){
16538         
16539         if(q === undefined || q === null){
16540             q = '';
16541         }
16542         var qe = {
16543             query: q,
16544             forceAll: forceAll,
16545             combo: this,
16546             cancel:false
16547         };
16548         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16549             return false;
16550         }
16551         q = qe.query;
16552         
16553         forceAll = qe.forceAll;
16554         if(forceAll === true || (q.length >= this.minChars)){
16555             
16556             this.hasQuery = true;
16557             
16558             if(this.lastQuery != q || this.alwaysQuery){
16559                 this.lastQuery = q;
16560                 if(this.mode == 'local'){
16561                     this.selectedIndex = -1;
16562                     if(forceAll){
16563                         this.store.clearFilter();
16564                     }else{
16565                         
16566                         if(this.specialFilter){
16567                             this.fireEvent('specialfilter', this);
16568                             this.onLoad();
16569                             return;
16570                         }
16571                         
16572                         this.store.filter(this.displayField, q);
16573                     }
16574                     
16575                     this.store.fireEvent("datachanged", this.store);
16576                     
16577                     this.onLoad();
16578                     
16579                     
16580                 }else{
16581                     
16582                     this.store.baseParams[this.queryParam] = q;
16583                     
16584                     var options = {params : this.getParams(q)};
16585                     
16586                     if(this.loadNext){
16587                         options.add = true;
16588                         options.params.start = this.page * this.pageSize;
16589                     }
16590                     
16591                     this.store.load(options);
16592                     
16593                     /*
16594                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16595                      *  we should expand the list on onLoad
16596                      *  so command out it
16597                      */
16598 //                    this.expand();
16599                 }
16600             }else{
16601                 this.selectedIndex = -1;
16602                 this.onLoad();   
16603             }
16604         }
16605         
16606         this.loadNext = false;
16607     },
16608     
16609     // private
16610     getParams : function(q){
16611         var p = {};
16612         //p[this.queryParam] = q;
16613         
16614         if(this.pageSize){
16615             p.start = 0;
16616             p.limit = this.pageSize;
16617         }
16618         return p;
16619     },
16620
16621     /**
16622      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16623      */
16624     collapse : function(){
16625         if(!this.isExpanded()){
16626             return;
16627         }
16628         
16629         this.list.hide();
16630         
16631         this.hasFocus = false;
16632         
16633         if(this.tickable){
16634             this.okBtn.hide();
16635             this.cancelBtn.hide();
16636             this.trigger.show();
16637             
16638             if(this.editable){
16639                 this.tickableInputEl().dom.value = '';
16640                 this.tickableInputEl().blur();
16641             }
16642             
16643         }
16644         
16645         Roo.get(document).un('mousedown', this.collapseIf, this);
16646         Roo.get(document).un('mousewheel', this.collapseIf, this);
16647         if (!this.editable) {
16648             Roo.get(document).un('keydown', this.listKeyPress, this);
16649         }
16650         this.fireEvent('collapse', this);
16651         
16652         this.validate();
16653     },
16654
16655     // private
16656     collapseIf : function(e){
16657         var in_combo  = e.within(this.el);
16658         var in_list =  e.within(this.list);
16659         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16660         
16661         if (in_combo || in_list || is_list) {
16662             //e.stopPropagation();
16663             return;
16664         }
16665         
16666         if(this.tickable){
16667             this.onTickableFooterButtonClick(e, false, false);
16668         }
16669
16670         this.collapse();
16671         
16672     },
16673
16674     /**
16675      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16676      */
16677     expand : function(){
16678        
16679         if(this.isExpanded() || !this.hasFocus){
16680             return;
16681         }
16682         
16683         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16684         this.list.setWidth(lw);
16685         
16686         Roo.log('expand');
16687         
16688         this.list.show();
16689         
16690         this.restrictHeight();
16691         
16692         if(this.tickable){
16693             
16694             this.tickItems = Roo.apply([], this.item);
16695             
16696             this.okBtn.show();
16697             this.cancelBtn.show();
16698             this.trigger.hide();
16699             
16700             if(this.editable){
16701                 this.tickableInputEl().focus();
16702             }
16703             
16704         }
16705         
16706         Roo.get(document).on('mousedown', this.collapseIf, this);
16707         Roo.get(document).on('mousewheel', this.collapseIf, this);
16708         if (!this.editable) {
16709             Roo.get(document).on('keydown', this.listKeyPress, this);
16710         }
16711         
16712         this.fireEvent('expand', this);
16713     },
16714
16715     // private
16716     // Implements the default empty TriggerField.onTriggerClick function
16717     onTriggerClick : function(e)
16718     {
16719         Roo.log('trigger click');
16720         
16721         if(this.disabled || !this.triggerList){
16722             return;
16723         }
16724         
16725         this.page = 0;
16726         this.loadNext = false;
16727         
16728         if(this.isExpanded()){
16729             this.collapse();
16730             if (!this.blockFocus) {
16731                 this.inputEl().focus();
16732             }
16733             
16734         }else {
16735             this.hasFocus = true;
16736             if(this.triggerAction == 'all') {
16737                 this.doQuery(this.allQuery, true);
16738             } else {
16739                 this.doQuery(this.getRawValue());
16740             }
16741             if (!this.blockFocus) {
16742                 this.inputEl().focus();
16743             }
16744         }
16745     },
16746     
16747     onTickableTriggerClick : function(e)
16748     {
16749         if(this.disabled){
16750             return;
16751         }
16752         
16753         this.page = 0;
16754         this.loadNext = false;
16755         this.hasFocus = true;
16756         
16757         if(this.triggerAction == 'all') {
16758             this.doQuery(this.allQuery, true);
16759         } else {
16760             this.doQuery(this.getRawValue());
16761         }
16762     },
16763     
16764     onSearchFieldClick : function(e)
16765     {
16766         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16767             this.onTickableFooterButtonClick(e, false, false);
16768             return;
16769         }
16770         
16771         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16772             return;
16773         }
16774         
16775         this.page = 0;
16776         this.loadNext = false;
16777         this.hasFocus = true;
16778         
16779         if(this.triggerAction == 'all') {
16780             this.doQuery(this.allQuery, true);
16781         } else {
16782             this.doQuery(this.getRawValue());
16783         }
16784     },
16785     
16786     listKeyPress : function(e)
16787     {
16788         //Roo.log('listkeypress');
16789         // scroll to first matching element based on key pres..
16790         if (e.isSpecialKey()) {
16791             return false;
16792         }
16793         var k = String.fromCharCode(e.getKey()).toUpperCase();
16794         //Roo.log(k);
16795         var match  = false;
16796         var csel = this.view.getSelectedNodes();
16797         var cselitem = false;
16798         if (csel.length) {
16799             var ix = this.view.indexOf(csel[0]);
16800             cselitem  = this.store.getAt(ix);
16801             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16802                 cselitem = false;
16803             }
16804             
16805         }
16806         
16807         this.store.each(function(v) { 
16808             if (cselitem) {
16809                 // start at existing selection.
16810                 if (cselitem.id == v.id) {
16811                     cselitem = false;
16812                 }
16813                 return true;
16814             }
16815                 
16816             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16817                 match = this.store.indexOf(v);
16818                 return false;
16819             }
16820             return true;
16821         }, this);
16822         
16823         if (match === false) {
16824             return true; // no more action?
16825         }
16826         // scroll to?
16827         this.view.select(match);
16828         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16829         sn.scrollIntoView(sn.dom.parentNode, false);
16830     },
16831     
16832     onViewScroll : function(e, t){
16833         
16834         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){
16835             return;
16836         }
16837         
16838         this.hasQuery = true;
16839         
16840         this.loading = this.list.select('.loading', true).first();
16841         
16842         if(this.loading === null){
16843             this.list.createChild({
16844                 tag: 'div',
16845                 cls: 'loading roo-select2-more-results roo-select2-active',
16846                 html: 'Loading more results...'
16847             });
16848             
16849             this.loading = this.list.select('.loading', true).first();
16850             
16851             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16852             
16853             this.loading.hide();
16854         }
16855         
16856         this.loading.show();
16857         
16858         var _combo = this;
16859         
16860         this.page++;
16861         this.loadNext = true;
16862         
16863         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16864         
16865         return;
16866     },
16867     
16868     addItem : function(o)
16869     {   
16870         var dv = ''; // display value
16871         
16872         if (this.displayField) {
16873             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16874         } else {
16875             // this is an error condition!!!
16876             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16877         }
16878         
16879         if(!dv.length){
16880             return;
16881         }
16882         
16883         var choice = this.choices.createChild({
16884             tag: 'li',
16885             cls: 'roo-select2-search-choice',
16886             cn: [
16887                 {
16888                     tag: 'div',
16889                     html: dv
16890                 },
16891                 {
16892                     tag: 'a',
16893                     href: '#',
16894                     cls: 'roo-select2-search-choice-close fa fa-times',
16895                     tabindex: '-1'
16896                 }
16897             ]
16898             
16899         }, this.searchField);
16900         
16901         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16902         
16903         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16904         
16905         this.item.push(o);
16906         
16907         this.lastData = o;
16908         
16909         this.syncValue();
16910         
16911         this.inputEl().dom.value = '';
16912         
16913         this.validate();
16914     },
16915     
16916     onRemoveItem : function(e, _self, o)
16917     {
16918         e.preventDefault();
16919         
16920         this.lastItem = Roo.apply([], this.item);
16921         
16922         var index = this.item.indexOf(o.data) * 1;
16923         
16924         if( index < 0){
16925             Roo.log('not this item?!');
16926             return;
16927         }
16928         
16929         this.item.splice(index, 1);
16930         o.item.remove();
16931         
16932         this.syncValue();
16933         
16934         this.fireEvent('remove', this, e);
16935         
16936         this.validate();
16937         
16938     },
16939     
16940     syncValue : function()
16941     {
16942         if(!this.item.length){
16943             this.clearValue();
16944             return;
16945         }
16946             
16947         var value = [];
16948         var _this = this;
16949         Roo.each(this.item, function(i){
16950             if(_this.valueField){
16951                 value.push(i[_this.valueField]);
16952                 return;
16953             }
16954
16955             value.push(i);
16956         });
16957
16958         this.value = value.join(',');
16959
16960         if(this.hiddenField){
16961             this.hiddenField.dom.value = this.value;
16962         }
16963         
16964         this.store.fireEvent("datachanged", this.store);
16965         
16966         this.validate();
16967     },
16968     
16969     clearItem : function()
16970     {
16971         if(!this.multiple){
16972             return;
16973         }
16974         
16975         this.item = [];
16976         
16977         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16978            c.remove();
16979         });
16980         
16981         this.syncValue();
16982         
16983         this.validate();
16984         
16985         if(this.tickable && !Roo.isTouch){
16986             this.view.refresh();
16987         }
16988     },
16989     
16990     inputEl: function ()
16991     {
16992         if(Roo.isIOS && this.useNativeIOS){
16993             return this.el.select('select.roo-ios-select', true).first();
16994         }
16995         
16996         if(Roo.isTouch && this.mobileTouchView){
16997             return this.el.select('input.form-control',true).first();
16998         }
16999         
17000         if(this.tickable){
17001             return this.searchField;
17002         }
17003         
17004         return this.el.select('input.form-control',true).first();
17005     },
17006     
17007     onTickableFooterButtonClick : function(e, btn, el)
17008     {
17009         e.preventDefault();
17010         
17011         this.lastItem = Roo.apply([], this.item);
17012         
17013         if(btn && btn.name == 'cancel'){
17014             this.tickItems = Roo.apply([], this.item);
17015             this.collapse();
17016             return;
17017         }
17018         
17019         this.clearItem();
17020         
17021         var _this = this;
17022         
17023         Roo.each(this.tickItems, function(o){
17024             _this.addItem(o);
17025         });
17026         
17027         this.collapse();
17028         
17029     },
17030     
17031     validate : function()
17032     {
17033         if(this.getVisibilityEl().hasClass('hidden')){
17034             return true;
17035         }
17036         
17037         var v = this.getRawValue();
17038         
17039         if(this.multiple){
17040             v = this.getValue();
17041         }
17042         
17043         if(this.disabled || this.allowBlank || v.length){
17044             this.markValid();
17045             return true;
17046         }
17047         
17048         this.markInvalid();
17049         return false;
17050     },
17051     
17052     tickableInputEl : function()
17053     {
17054         if(!this.tickable || !this.editable){
17055             return this.inputEl();
17056         }
17057         
17058         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17059     },
17060     
17061     
17062     getAutoCreateTouchView : function()
17063     {
17064         var id = Roo.id();
17065         
17066         var cfg = {
17067             cls: 'form-group' //input-group
17068         };
17069         
17070         var input =  {
17071             tag: 'input',
17072             id : id,
17073             type : this.inputType,
17074             cls : 'form-control x-combo-noedit',
17075             autocomplete: 'new-password',
17076             placeholder : this.placeholder || '',
17077             readonly : true
17078         };
17079         
17080         if (this.name) {
17081             input.name = this.name;
17082         }
17083         
17084         if (this.size) {
17085             input.cls += ' input-' + this.size;
17086         }
17087         
17088         if (this.disabled) {
17089             input.disabled = true;
17090         }
17091         
17092         var inputblock = {
17093             cls : 'roo-combobox-wrap',
17094             cn : [
17095                 input
17096             ]
17097         };
17098         
17099         if(this.before){
17100             inputblock.cls += ' input-group';
17101             
17102             inputblock.cn.unshift({
17103                 tag :'span',
17104                 cls : 'input-group-addon input-group-prepend input-group-text',
17105                 html : this.before
17106             });
17107         }
17108         
17109         if(this.removable && !this.multiple){
17110             inputblock.cls += ' roo-removable';
17111             
17112             inputblock.cn.push({
17113                 tag: 'button',
17114                 html : 'x',
17115                 cls : 'roo-combo-removable-btn close'
17116             });
17117         }
17118
17119         if(this.hasFeedback && !this.allowBlank){
17120             
17121             inputblock.cls += ' has-feedback';
17122             
17123             inputblock.cn.push({
17124                 tag: 'span',
17125                 cls: 'glyphicon form-control-feedback'
17126             });
17127             
17128         }
17129         
17130         if (this.after) {
17131             
17132             inputblock.cls += (this.before) ? '' : ' input-group';
17133             
17134             inputblock.cn.push({
17135                 tag :'span',
17136                 cls : 'input-group-addon input-group-append input-group-text',
17137                 html : this.after
17138             });
17139         }
17140
17141         
17142         var ibwrap = inputblock;
17143         
17144         if(this.multiple){
17145             ibwrap = {
17146                 tag: 'ul',
17147                 cls: 'roo-select2-choices',
17148                 cn:[
17149                     {
17150                         tag: 'li',
17151                         cls: 'roo-select2-search-field',
17152                         cn: [
17153
17154                             inputblock
17155                         ]
17156                     }
17157                 ]
17158             };
17159         
17160             
17161         }
17162         
17163         var combobox = {
17164             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17165             cn: [
17166                 {
17167                     tag: 'input',
17168                     type : 'hidden',
17169                     cls: 'form-hidden-field'
17170                 },
17171                 ibwrap
17172             ]
17173         };
17174         
17175         if(!this.multiple && this.showToggleBtn){
17176             
17177             var caret = {
17178                 cls: 'caret'
17179             };
17180             
17181             if (this.caret != false) {
17182                 caret = {
17183                      tag: 'i',
17184                      cls: 'fa fa-' + this.caret
17185                 };
17186                 
17187             }
17188             
17189             combobox.cn.push({
17190                 tag :'span',
17191                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17192                 cn : [
17193                     Roo.bootstrap.version == 3 ? caret : '',
17194                     {
17195                         tag: 'span',
17196                         cls: 'combobox-clear',
17197                         cn  : [
17198                             {
17199                                 tag : 'i',
17200                                 cls: 'icon-remove'
17201                             }
17202                         ]
17203                     }
17204                 ]
17205
17206             })
17207         }
17208         
17209         if(this.multiple){
17210             combobox.cls += ' roo-select2-container-multi';
17211         }
17212         
17213         var align = this.labelAlign || this.parentLabelAlign();
17214         
17215         if (align ==='left' && this.fieldLabel.length) {
17216
17217             cfg.cn = [
17218                 {
17219                    tag : 'i',
17220                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17221                    tooltip : 'This field is required'
17222                 },
17223                 {
17224                     tag: 'label',
17225                     cls : 'control-label col-form-label',
17226                     html : this.fieldLabel
17227
17228                 },
17229                 {
17230                     cls : 'roo-combobox-wrap ', 
17231                     cn: [
17232                         combobox
17233                     ]
17234                 }
17235             ];
17236             
17237             var labelCfg = cfg.cn[1];
17238             var contentCfg = cfg.cn[2];
17239             
17240
17241             if(this.indicatorpos == 'right'){
17242                 cfg.cn = [
17243                     {
17244                         tag: 'label',
17245                         'for' :  id,
17246                         cls : 'control-label col-form-label',
17247                         cn : [
17248                             {
17249                                 tag : 'span',
17250                                 html : this.fieldLabel
17251                             },
17252                             {
17253                                 tag : 'i',
17254                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17255                                 tooltip : 'This field is required'
17256                             }
17257                         ]
17258                     },
17259                     {
17260                         cls : "roo-combobox-wrap ",
17261                         cn: [
17262                             combobox
17263                         ]
17264                     }
17265
17266                 ];
17267                 
17268                 labelCfg = cfg.cn[0];
17269                 contentCfg = cfg.cn[1];
17270             }
17271             
17272            
17273             
17274             if(this.labelWidth > 12){
17275                 labelCfg.style = "width: " + this.labelWidth + 'px';
17276             }
17277            
17278             if(this.labelWidth < 13 && this.labelmd == 0){
17279                 this.labelmd = this.labelWidth;
17280             }
17281             
17282             if(this.labellg > 0){
17283                 labelCfg.cls += ' col-lg-' + this.labellg;
17284                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17285             }
17286             
17287             if(this.labelmd > 0){
17288                 labelCfg.cls += ' col-md-' + this.labelmd;
17289                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17290             }
17291             
17292             if(this.labelsm > 0){
17293                 labelCfg.cls += ' col-sm-' + this.labelsm;
17294                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17295             }
17296             
17297             if(this.labelxs > 0){
17298                 labelCfg.cls += ' col-xs-' + this.labelxs;
17299                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17300             }
17301                 
17302                 
17303         } else if ( this.fieldLabel.length) {
17304             cfg.cn = [
17305                 {
17306                    tag : 'i',
17307                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17308                    tooltip : 'This field is required'
17309                 },
17310                 {
17311                     tag: 'label',
17312                     cls : 'control-label',
17313                     html : this.fieldLabel
17314
17315                 },
17316                 {
17317                     cls : '', 
17318                     cn: [
17319                         combobox
17320                     ]
17321                 }
17322             ];
17323             
17324             if(this.indicatorpos == 'right'){
17325                 cfg.cn = [
17326                     {
17327                         tag: 'label',
17328                         cls : 'control-label',
17329                         html : this.fieldLabel,
17330                         cn : [
17331                             {
17332                                tag : 'i',
17333                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17334                                tooltip : 'This field is required'
17335                             }
17336                         ]
17337                     },
17338                     {
17339                         cls : '', 
17340                         cn: [
17341                             combobox
17342                         ]
17343                     }
17344                 ];
17345             }
17346         } else {
17347             cfg.cn = combobox;    
17348         }
17349         
17350         
17351         var settings = this;
17352         
17353         ['xs','sm','md','lg'].map(function(size){
17354             if (settings[size]) {
17355                 cfg.cls += ' col-' + size + '-' + settings[size];
17356             }
17357         });
17358         
17359         return cfg;
17360     },
17361     
17362     initTouchView : function()
17363     {
17364         this.renderTouchView();
17365         
17366         this.touchViewEl.on('scroll', function(){
17367             this.el.dom.scrollTop = 0;
17368         }, this);
17369         
17370         this.originalValue = this.getValue();
17371         
17372         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17373         
17374         this.inputEl().on("click", this.showTouchView, this);
17375         if (this.triggerEl) {
17376             this.triggerEl.on("click", this.showTouchView, this);
17377         }
17378         
17379         
17380         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17381         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17382         
17383         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17384         
17385         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17386         this.store.on('load', this.onTouchViewLoad, this);
17387         this.store.on('loadexception', this.onTouchViewLoadException, this);
17388         
17389         if(this.hiddenName){
17390             
17391             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17392             
17393             this.hiddenField.dom.value =
17394                 this.hiddenValue !== undefined ? this.hiddenValue :
17395                 this.value !== undefined ? this.value : '';
17396         
17397             this.el.dom.removeAttribute('name');
17398             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17399         }
17400         
17401         if(this.multiple){
17402             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17403             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17404         }
17405         
17406         if(this.removable && !this.multiple){
17407             var close = this.closeTriggerEl();
17408             if(close){
17409                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17410                 close.on('click', this.removeBtnClick, this, close);
17411             }
17412         }
17413         /*
17414          * fix the bug in Safari iOS8
17415          */
17416         this.inputEl().on("focus", function(e){
17417             document.activeElement.blur();
17418         }, this);
17419         
17420         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17421         
17422         return;
17423         
17424         
17425     },
17426     
17427     renderTouchView : function()
17428     {
17429         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17430         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17431         
17432         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17433         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17434         
17435         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17436         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17437         this.touchViewBodyEl.setStyle('overflow', 'auto');
17438         
17439         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17440         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17441         
17442         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17443         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17444         
17445     },
17446     
17447     showTouchView : function()
17448     {
17449         if(this.disabled){
17450             return;
17451         }
17452         
17453         this.touchViewHeaderEl.hide();
17454
17455         if(this.modalTitle.length){
17456             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17457             this.touchViewHeaderEl.show();
17458         }
17459
17460         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17461         this.touchViewEl.show();
17462
17463         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17464         
17465         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17466         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17467
17468         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17469
17470         if(this.modalTitle.length){
17471             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17472         }
17473         
17474         this.touchViewBodyEl.setHeight(bodyHeight);
17475
17476         if(this.animate){
17477             var _this = this;
17478             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17479         }else{
17480             this.touchViewEl.addClass(['in','show']);
17481         }
17482         
17483         if(this._touchViewMask){
17484             Roo.get(document.body).addClass("x-body-masked");
17485             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17486             this._touchViewMask.setStyle('z-index', 10000);
17487             this._touchViewMask.addClass('show');
17488         }
17489         
17490         this.doTouchViewQuery();
17491         
17492     },
17493     
17494     hideTouchView : function()
17495     {
17496         this.touchViewEl.removeClass(['in','show']);
17497
17498         if(this.animate){
17499             var _this = this;
17500             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17501         }else{
17502             this.touchViewEl.setStyle('display', 'none');
17503         }
17504         
17505         if(this._touchViewMask){
17506             this._touchViewMask.removeClass('show');
17507             Roo.get(document.body).removeClass("x-body-masked");
17508         }
17509     },
17510     
17511     setTouchViewValue : function()
17512     {
17513         if(this.multiple){
17514             this.clearItem();
17515         
17516             var _this = this;
17517
17518             Roo.each(this.tickItems, function(o){
17519                 this.addItem(o);
17520             }, this);
17521         }
17522         
17523         this.hideTouchView();
17524     },
17525     
17526     doTouchViewQuery : function()
17527     {
17528         var qe = {
17529             query: '',
17530             forceAll: true,
17531             combo: this,
17532             cancel:false
17533         };
17534         
17535         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17536             return false;
17537         }
17538         
17539         if(!this.alwaysQuery || this.mode == 'local'){
17540             this.onTouchViewLoad();
17541             return;
17542         }
17543         
17544         this.store.load();
17545     },
17546     
17547     onTouchViewBeforeLoad : function(combo,opts)
17548     {
17549         return;
17550     },
17551
17552     // private
17553     onTouchViewLoad : function()
17554     {
17555         if(this.store.getCount() < 1){
17556             this.onTouchViewEmptyResults();
17557             return;
17558         }
17559         
17560         this.clearTouchView();
17561         
17562         var rawValue = this.getRawValue();
17563         
17564         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17565         
17566         this.tickItems = [];
17567         
17568         this.store.data.each(function(d, rowIndex){
17569             var row = this.touchViewListGroup.createChild(template);
17570             
17571             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17572                 row.addClass(d.data.cls);
17573             }
17574             
17575             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17576                 var cfg = {
17577                     data : d.data,
17578                     html : d.data[this.displayField]
17579                 };
17580                 
17581                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17582                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17583                 }
17584             }
17585             row.removeClass('selected');
17586             if(!this.multiple && this.valueField &&
17587                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17588             {
17589                 // radio buttons..
17590                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17591                 row.addClass('selected');
17592             }
17593             
17594             if(this.multiple && this.valueField &&
17595                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17596             {
17597                 
17598                 // checkboxes...
17599                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17600                 this.tickItems.push(d.data);
17601             }
17602             
17603             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17604             
17605         }, this);
17606         
17607         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17608         
17609         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17610
17611         if(this.modalTitle.length){
17612             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17613         }
17614
17615         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17616         
17617         if(this.mobile_restrict_height && listHeight < bodyHeight){
17618             this.touchViewBodyEl.setHeight(listHeight);
17619         }
17620         
17621         var _this = this;
17622         
17623         if(firstChecked && listHeight > bodyHeight){
17624             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17625         }
17626         
17627     },
17628     
17629     onTouchViewLoadException : function()
17630     {
17631         this.hideTouchView();
17632     },
17633     
17634     onTouchViewEmptyResults : function()
17635     {
17636         this.clearTouchView();
17637         
17638         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17639         
17640         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17641         
17642     },
17643     
17644     clearTouchView : function()
17645     {
17646         this.touchViewListGroup.dom.innerHTML = '';
17647     },
17648     
17649     onTouchViewClick : function(e, el, o)
17650     {
17651         e.preventDefault();
17652         
17653         var row = o.row;
17654         var rowIndex = o.rowIndex;
17655         
17656         var r = this.store.getAt(rowIndex);
17657         
17658         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17659             
17660             if(!this.multiple){
17661                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17662                     c.dom.removeAttribute('checked');
17663                 }, this);
17664
17665                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17666
17667                 this.setFromData(r.data);
17668
17669                 var close = this.closeTriggerEl();
17670
17671                 if(close){
17672                     close.show();
17673                 }
17674
17675                 this.hideTouchView();
17676
17677                 this.fireEvent('select', this, r, rowIndex);
17678
17679                 return;
17680             }
17681
17682             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17683                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17684                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17685                 return;
17686             }
17687
17688             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17689             this.addItem(r.data);
17690             this.tickItems.push(r.data);
17691         }
17692     },
17693     
17694     getAutoCreateNativeIOS : function()
17695     {
17696         var cfg = {
17697             cls: 'form-group' //input-group,
17698         };
17699         
17700         var combobox =  {
17701             tag: 'select',
17702             cls : 'roo-ios-select'
17703         };
17704         
17705         if (this.name) {
17706             combobox.name = this.name;
17707         }
17708         
17709         if (this.disabled) {
17710             combobox.disabled = true;
17711         }
17712         
17713         var settings = this;
17714         
17715         ['xs','sm','md','lg'].map(function(size){
17716             if (settings[size]) {
17717                 cfg.cls += ' col-' + size + '-' + settings[size];
17718             }
17719         });
17720         
17721         cfg.cn = combobox;
17722         
17723         return cfg;
17724         
17725     },
17726     
17727     initIOSView : function()
17728     {
17729         this.store.on('load', this.onIOSViewLoad, this);
17730         
17731         return;
17732     },
17733     
17734     onIOSViewLoad : function()
17735     {
17736         if(this.store.getCount() < 1){
17737             return;
17738         }
17739         
17740         this.clearIOSView();
17741         
17742         if(this.allowBlank) {
17743             
17744             var default_text = '-- SELECT --';
17745             
17746             if(this.placeholder.length){
17747                 default_text = this.placeholder;
17748             }
17749             
17750             if(this.emptyTitle.length){
17751                 default_text += ' - ' + this.emptyTitle + ' -';
17752             }
17753             
17754             var opt = this.inputEl().createChild({
17755                 tag: 'option',
17756                 value : 0,
17757                 html : default_text
17758             });
17759             
17760             var o = {};
17761             o[this.valueField] = 0;
17762             o[this.displayField] = default_text;
17763             
17764             this.ios_options.push({
17765                 data : o,
17766                 el : opt
17767             });
17768             
17769         }
17770         
17771         this.store.data.each(function(d, rowIndex){
17772             
17773             var html = '';
17774             
17775             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17776                 html = d.data[this.displayField];
17777             }
17778             
17779             var value = '';
17780             
17781             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17782                 value = d.data[this.valueField];
17783             }
17784             
17785             var option = {
17786                 tag: 'option',
17787                 value : value,
17788                 html : html
17789             };
17790             
17791             if(this.value == d.data[this.valueField]){
17792                 option['selected'] = true;
17793             }
17794             
17795             var opt = this.inputEl().createChild(option);
17796             
17797             this.ios_options.push({
17798                 data : d.data,
17799                 el : opt
17800             });
17801             
17802         }, this);
17803         
17804         this.inputEl().on('change', function(){
17805            this.fireEvent('select', this);
17806         }, this);
17807         
17808     },
17809     
17810     clearIOSView: function()
17811     {
17812         this.inputEl().dom.innerHTML = '';
17813         
17814         this.ios_options = [];
17815     },
17816     
17817     setIOSValue: function(v)
17818     {
17819         this.value = v;
17820         
17821         if(!this.ios_options){
17822             return;
17823         }
17824         
17825         Roo.each(this.ios_options, function(opts){
17826            
17827            opts.el.dom.removeAttribute('selected');
17828            
17829            if(opts.data[this.valueField] != v){
17830                return;
17831            }
17832            
17833            opts.el.dom.setAttribute('selected', true);
17834            
17835         }, this);
17836     }
17837
17838     /** 
17839     * @cfg {Boolean} grow 
17840     * @hide 
17841     */
17842     /** 
17843     * @cfg {Number} growMin 
17844     * @hide 
17845     */
17846     /** 
17847     * @cfg {Number} growMax 
17848     * @hide 
17849     */
17850     /**
17851      * @hide
17852      * @method autoSize
17853      */
17854 });
17855
17856 Roo.apply(Roo.bootstrap.ComboBox,  {
17857     
17858     header : {
17859         tag: 'div',
17860         cls: 'modal-header',
17861         cn: [
17862             {
17863                 tag: 'h4',
17864                 cls: 'modal-title'
17865             }
17866         ]
17867     },
17868     
17869     body : {
17870         tag: 'div',
17871         cls: 'modal-body',
17872         cn: [
17873             {
17874                 tag: 'ul',
17875                 cls: 'list-group'
17876             }
17877         ]
17878     },
17879     
17880     listItemRadio : {
17881         tag: 'li',
17882         cls: 'list-group-item',
17883         cn: [
17884             {
17885                 tag: 'span',
17886                 cls: 'roo-combobox-list-group-item-value'
17887             },
17888             {
17889                 tag: 'div',
17890                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17891                 cn: [
17892                     {
17893                         tag: 'input',
17894                         type: 'radio'
17895                     },
17896                     {
17897                         tag: 'label'
17898                     }
17899                 ]
17900             }
17901         ]
17902     },
17903     
17904     listItemCheckbox : {
17905         tag: 'li',
17906         cls: 'list-group-item',
17907         cn: [
17908             {
17909                 tag: 'span',
17910                 cls: 'roo-combobox-list-group-item-value'
17911             },
17912             {
17913                 tag: 'div',
17914                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17915                 cn: [
17916                     {
17917                         tag: 'input',
17918                         type: 'checkbox'
17919                     },
17920                     {
17921                         tag: 'label'
17922                     }
17923                 ]
17924             }
17925         ]
17926     },
17927     
17928     emptyResult : {
17929         tag: 'div',
17930         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17931     },
17932     
17933     footer : {
17934         tag: 'div',
17935         cls: 'modal-footer',
17936         cn: [
17937             {
17938                 tag: 'div',
17939                 cls: 'row',
17940                 cn: [
17941                     {
17942                         tag: 'div',
17943                         cls: 'col-xs-6 text-left',
17944                         cn: {
17945                             tag: 'button',
17946                             cls: 'btn btn-danger roo-touch-view-cancel',
17947                             html: 'Cancel'
17948                         }
17949                     },
17950                     {
17951                         tag: 'div',
17952                         cls: 'col-xs-6 text-right',
17953                         cn: {
17954                             tag: 'button',
17955                             cls: 'btn btn-success roo-touch-view-ok',
17956                             html: 'OK'
17957                         }
17958                     }
17959                 ]
17960             }
17961         ]
17962         
17963     }
17964 });
17965
17966 Roo.apply(Roo.bootstrap.ComboBox,  {
17967     
17968     touchViewTemplate : {
17969         tag: 'div',
17970         cls: 'modal fade roo-combobox-touch-view',
17971         cn: [
17972             {
17973                 tag: 'div',
17974                 cls: 'modal-dialog',
17975                 style : 'position:fixed', // we have to fix position....
17976                 cn: [
17977                     {
17978                         tag: 'div',
17979                         cls: 'modal-content',
17980                         cn: [
17981                             Roo.bootstrap.ComboBox.header,
17982                             Roo.bootstrap.ComboBox.body,
17983                             Roo.bootstrap.ComboBox.footer
17984                         ]
17985                     }
17986                 ]
17987             }
17988         ]
17989     }
17990 });/*
17991  * Based on:
17992  * Ext JS Library 1.1.1
17993  * Copyright(c) 2006-2007, Ext JS, LLC.
17994  *
17995  * Originally Released Under LGPL - original licence link has changed is not relivant.
17996  *
17997  * Fork - LGPL
17998  * <script type="text/javascript">
17999  */
18000
18001 /**
18002  * @class Roo.View
18003  * @extends Roo.util.Observable
18004  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18005  * This class also supports single and multi selection modes. <br>
18006  * Create a data model bound view:
18007  <pre><code>
18008  var store = new Roo.data.Store(...);
18009
18010  var view = new Roo.View({
18011     el : "my-element",
18012     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18013  
18014     singleSelect: true,
18015     selectedClass: "ydataview-selected",
18016     store: store
18017  });
18018
18019  // listen for node click?
18020  view.on("click", function(vw, index, node, e){
18021  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18022  });
18023
18024  // load XML data
18025  dataModel.load("foobar.xml");
18026  </code></pre>
18027  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18028  * <br><br>
18029  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18030  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18031  * 
18032  * Note: old style constructor is still suported (container, template, config)
18033  * 
18034  * @constructor
18035  * Create a new View
18036  * @param {Object} config The config object
18037  * 
18038  */
18039 Roo.View = function(config, depreciated_tpl, depreciated_config){
18040     
18041     this.parent = false;
18042     
18043     if (typeof(depreciated_tpl) == 'undefined') {
18044         // new way.. - universal constructor.
18045         Roo.apply(this, config);
18046         this.el  = Roo.get(this.el);
18047     } else {
18048         // old format..
18049         this.el  = Roo.get(config);
18050         this.tpl = depreciated_tpl;
18051         Roo.apply(this, depreciated_config);
18052     }
18053     this.wrapEl  = this.el.wrap().wrap();
18054     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18055     
18056     
18057     if(typeof(this.tpl) == "string"){
18058         this.tpl = new Roo.Template(this.tpl);
18059     } else {
18060         // support xtype ctors..
18061         this.tpl = new Roo.factory(this.tpl, Roo);
18062     }
18063     
18064     
18065     this.tpl.compile();
18066     
18067     /** @private */
18068     this.addEvents({
18069         /**
18070          * @event beforeclick
18071          * Fires before a click is processed. Returns false to cancel the default action.
18072          * @param {Roo.View} this
18073          * @param {Number} index The index of the target node
18074          * @param {HTMLElement} node The target node
18075          * @param {Roo.EventObject} e The raw event object
18076          */
18077             "beforeclick" : true,
18078         /**
18079          * @event click
18080          * Fires when a template node is clicked.
18081          * @param {Roo.View} this
18082          * @param {Number} index The index of the target node
18083          * @param {HTMLElement} node The target node
18084          * @param {Roo.EventObject} e The raw event object
18085          */
18086             "click" : true,
18087         /**
18088          * @event dblclick
18089          * Fires when a template node is double clicked.
18090          * @param {Roo.View} this
18091          * @param {Number} index The index of the target node
18092          * @param {HTMLElement} node The target node
18093          * @param {Roo.EventObject} e The raw event object
18094          */
18095             "dblclick" : true,
18096         /**
18097          * @event contextmenu
18098          * Fires when a template node is right clicked.
18099          * @param {Roo.View} this
18100          * @param {Number} index The index of the target node
18101          * @param {HTMLElement} node The target node
18102          * @param {Roo.EventObject} e The raw event object
18103          */
18104             "contextmenu" : true,
18105         /**
18106          * @event selectionchange
18107          * Fires when the selected nodes change.
18108          * @param {Roo.View} this
18109          * @param {Array} selections Array of the selected nodes
18110          */
18111             "selectionchange" : true,
18112     
18113         /**
18114          * @event beforeselect
18115          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18116          * @param {Roo.View} this
18117          * @param {HTMLElement} node The node to be selected
18118          * @param {Array} selections Array of currently selected nodes
18119          */
18120             "beforeselect" : true,
18121         /**
18122          * @event preparedata
18123          * Fires on every row to render, to allow you to change the data.
18124          * @param {Roo.View} this
18125          * @param {Object} data to be rendered (change this)
18126          */
18127           "preparedata" : true
18128           
18129           
18130         });
18131
18132
18133
18134     this.el.on({
18135         "click": this.onClick,
18136         "dblclick": this.onDblClick,
18137         "contextmenu": this.onContextMenu,
18138         scope:this
18139     });
18140
18141     this.selections = [];
18142     this.nodes = [];
18143     this.cmp = new Roo.CompositeElementLite([]);
18144     if(this.store){
18145         this.store = Roo.factory(this.store, Roo.data);
18146         this.setStore(this.store, true);
18147     }
18148     
18149     if ( this.footer && this.footer.xtype) {
18150            
18151          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18152         
18153         this.footer.dataSource = this.store;
18154         this.footer.container = fctr;
18155         this.footer = Roo.factory(this.footer, Roo);
18156         fctr.insertFirst(this.el);
18157         
18158         // this is a bit insane - as the paging toolbar seems to detach the el..
18159 //        dom.parentNode.parentNode.parentNode
18160          // they get detached?
18161     }
18162     
18163     
18164     Roo.View.superclass.constructor.call(this);
18165     
18166     
18167 };
18168
18169 Roo.extend(Roo.View, Roo.util.Observable, {
18170     
18171      /**
18172      * @cfg {Roo.data.Store} store Data store to load data from.
18173      */
18174     store : false,
18175     
18176     /**
18177      * @cfg {String|Roo.Element} el The container element.
18178      */
18179     el : '',
18180     
18181     /**
18182      * @cfg {String|Roo.Template} tpl The template used by this View 
18183      */
18184     tpl : false,
18185     /**
18186      * @cfg {String} dataName the named area of the template to use as the data area
18187      *                          Works with domtemplates roo-name="name"
18188      */
18189     dataName: false,
18190     /**
18191      * @cfg {String} selectedClass The css class to add to selected nodes
18192      */
18193     selectedClass : "x-view-selected",
18194      /**
18195      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18196      */
18197     emptyText : "",
18198     
18199     /**
18200      * @cfg {String} text to display on mask (default Loading)
18201      */
18202     mask : false,
18203     /**
18204      * @cfg {Boolean} multiSelect Allow multiple selection
18205      */
18206     multiSelect : false,
18207     /**
18208      * @cfg {Boolean} singleSelect Allow single selection
18209      */
18210     singleSelect:  false,
18211     
18212     /**
18213      * @cfg {Boolean} toggleSelect - selecting 
18214      */
18215     toggleSelect : false,
18216     
18217     /**
18218      * @cfg {Boolean} tickable - selecting 
18219      */
18220     tickable : false,
18221     
18222     /**
18223      * Returns the element this view is bound to.
18224      * @return {Roo.Element}
18225      */
18226     getEl : function(){
18227         return this.wrapEl;
18228     },
18229     
18230     
18231
18232     /**
18233      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18234      */
18235     refresh : function(){
18236         //Roo.log('refresh');
18237         var t = this.tpl;
18238         
18239         // if we are using something like 'domtemplate', then
18240         // the what gets used is:
18241         // t.applySubtemplate(NAME, data, wrapping data..)
18242         // the outer template then get' applied with
18243         //     the store 'extra data'
18244         // and the body get's added to the
18245         //      roo-name="data" node?
18246         //      <span class='roo-tpl-{name}'></span> ?????
18247         
18248         
18249         
18250         this.clearSelections();
18251         this.el.update("");
18252         var html = [];
18253         var records = this.store.getRange();
18254         if(records.length < 1) {
18255             
18256             // is this valid??  = should it render a template??
18257             
18258             this.el.update(this.emptyText);
18259             return;
18260         }
18261         var el = this.el;
18262         if (this.dataName) {
18263             this.el.update(t.apply(this.store.meta)); //????
18264             el = this.el.child('.roo-tpl-' + this.dataName);
18265         }
18266         
18267         for(var i = 0, len = records.length; i < len; i++){
18268             var data = this.prepareData(records[i].data, i, records[i]);
18269             this.fireEvent("preparedata", this, data, i, records[i]);
18270             
18271             var d = Roo.apply({}, data);
18272             
18273             if(this.tickable){
18274                 Roo.apply(d, {'roo-id' : Roo.id()});
18275                 
18276                 var _this = this;
18277             
18278                 Roo.each(this.parent.item, function(item){
18279                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18280                         return;
18281                     }
18282                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18283                 });
18284             }
18285             
18286             html[html.length] = Roo.util.Format.trim(
18287                 this.dataName ?
18288                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18289                     t.apply(d)
18290             );
18291         }
18292         
18293         
18294         
18295         el.update(html.join(""));
18296         this.nodes = el.dom.childNodes;
18297         this.updateIndexes(0);
18298     },
18299     
18300
18301     /**
18302      * Function to override to reformat the data that is sent to
18303      * the template for each node.
18304      * DEPRICATED - use the preparedata event handler.
18305      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18306      * a JSON object for an UpdateManager bound view).
18307      */
18308     prepareData : function(data, index, record)
18309     {
18310         this.fireEvent("preparedata", this, data, index, record);
18311         return data;
18312     },
18313
18314     onUpdate : function(ds, record){
18315         // Roo.log('on update');   
18316         this.clearSelections();
18317         var index = this.store.indexOf(record);
18318         var n = this.nodes[index];
18319         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18320         n.parentNode.removeChild(n);
18321         this.updateIndexes(index, index);
18322     },
18323
18324     
18325     
18326 // --------- FIXME     
18327     onAdd : function(ds, records, index)
18328     {
18329         //Roo.log(['on Add', ds, records, index] );        
18330         this.clearSelections();
18331         if(this.nodes.length == 0){
18332             this.refresh();
18333             return;
18334         }
18335         var n = this.nodes[index];
18336         for(var i = 0, len = records.length; i < len; i++){
18337             var d = this.prepareData(records[i].data, i, records[i]);
18338             if(n){
18339                 this.tpl.insertBefore(n, d);
18340             }else{
18341                 
18342                 this.tpl.append(this.el, d);
18343             }
18344         }
18345         this.updateIndexes(index);
18346     },
18347
18348     onRemove : function(ds, record, index){
18349        // Roo.log('onRemove');
18350         this.clearSelections();
18351         var el = this.dataName  ?
18352             this.el.child('.roo-tpl-' + this.dataName) :
18353             this.el; 
18354         
18355         el.dom.removeChild(this.nodes[index]);
18356         this.updateIndexes(index);
18357     },
18358
18359     /**
18360      * Refresh an individual node.
18361      * @param {Number} index
18362      */
18363     refreshNode : function(index){
18364         this.onUpdate(this.store, this.store.getAt(index));
18365     },
18366
18367     updateIndexes : function(startIndex, endIndex){
18368         var ns = this.nodes;
18369         startIndex = startIndex || 0;
18370         endIndex = endIndex || ns.length - 1;
18371         for(var i = startIndex; i <= endIndex; i++){
18372             ns[i].nodeIndex = i;
18373         }
18374     },
18375
18376     /**
18377      * Changes the data store this view uses and refresh the view.
18378      * @param {Store} store
18379      */
18380     setStore : function(store, initial){
18381         if(!initial && this.store){
18382             this.store.un("datachanged", this.refresh);
18383             this.store.un("add", this.onAdd);
18384             this.store.un("remove", this.onRemove);
18385             this.store.un("update", this.onUpdate);
18386             this.store.un("clear", this.refresh);
18387             this.store.un("beforeload", this.onBeforeLoad);
18388             this.store.un("load", this.onLoad);
18389             this.store.un("loadexception", this.onLoad);
18390         }
18391         if(store){
18392           
18393             store.on("datachanged", this.refresh, this);
18394             store.on("add", this.onAdd, this);
18395             store.on("remove", this.onRemove, this);
18396             store.on("update", this.onUpdate, this);
18397             store.on("clear", this.refresh, this);
18398             store.on("beforeload", this.onBeforeLoad, this);
18399             store.on("load", this.onLoad, this);
18400             store.on("loadexception", this.onLoad, this);
18401         }
18402         
18403         if(store){
18404             this.refresh();
18405         }
18406     },
18407     /**
18408      * onbeforeLoad - masks the loading area.
18409      *
18410      */
18411     onBeforeLoad : function(store,opts)
18412     {
18413          //Roo.log('onBeforeLoad');   
18414         if (!opts.add) {
18415             this.el.update("");
18416         }
18417         this.el.mask(this.mask ? this.mask : "Loading" ); 
18418     },
18419     onLoad : function ()
18420     {
18421         this.el.unmask();
18422     },
18423     
18424
18425     /**
18426      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18427      * @param {HTMLElement} node
18428      * @return {HTMLElement} The template node
18429      */
18430     findItemFromChild : function(node){
18431         var el = this.dataName  ?
18432             this.el.child('.roo-tpl-' + this.dataName,true) :
18433             this.el.dom; 
18434         
18435         if(!node || node.parentNode == el){
18436                     return node;
18437             }
18438             var p = node.parentNode;
18439             while(p && p != el){
18440             if(p.parentNode == el){
18441                 return p;
18442             }
18443             p = p.parentNode;
18444         }
18445             return null;
18446     },
18447
18448     /** @ignore */
18449     onClick : function(e){
18450         var item = this.findItemFromChild(e.getTarget());
18451         if(item){
18452             var index = this.indexOf(item);
18453             if(this.onItemClick(item, index, e) !== false){
18454                 this.fireEvent("click", this, index, item, e);
18455             }
18456         }else{
18457             this.clearSelections();
18458         }
18459     },
18460
18461     /** @ignore */
18462     onContextMenu : function(e){
18463         var item = this.findItemFromChild(e.getTarget());
18464         if(item){
18465             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18466         }
18467     },
18468
18469     /** @ignore */
18470     onDblClick : function(e){
18471         var item = this.findItemFromChild(e.getTarget());
18472         if(item){
18473             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18474         }
18475     },
18476
18477     onItemClick : function(item, index, e)
18478     {
18479         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18480             return false;
18481         }
18482         if (this.toggleSelect) {
18483             var m = this.isSelected(item) ? 'unselect' : 'select';
18484             //Roo.log(m);
18485             var _t = this;
18486             _t[m](item, true, false);
18487             return true;
18488         }
18489         if(this.multiSelect || this.singleSelect){
18490             if(this.multiSelect && e.shiftKey && this.lastSelection){
18491                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18492             }else{
18493                 this.select(item, this.multiSelect && e.ctrlKey);
18494                 this.lastSelection = item;
18495             }
18496             
18497             if(!this.tickable){
18498                 e.preventDefault();
18499             }
18500             
18501         }
18502         return true;
18503     },
18504
18505     /**
18506      * Get the number of selected nodes.
18507      * @return {Number}
18508      */
18509     getSelectionCount : function(){
18510         return this.selections.length;
18511     },
18512
18513     /**
18514      * Get the currently selected nodes.
18515      * @return {Array} An array of HTMLElements
18516      */
18517     getSelectedNodes : function(){
18518         return this.selections;
18519     },
18520
18521     /**
18522      * Get the indexes of the selected nodes.
18523      * @return {Array}
18524      */
18525     getSelectedIndexes : function(){
18526         var indexes = [], s = this.selections;
18527         for(var i = 0, len = s.length; i < len; i++){
18528             indexes.push(s[i].nodeIndex);
18529         }
18530         return indexes;
18531     },
18532
18533     /**
18534      * Clear all selections
18535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18536      */
18537     clearSelections : function(suppressEvent){
18538         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18539             this.cmp.elements = this.selections;
18540             this.cmp.removeClass(this.selectedClass);
18541             this.selections = [];
18542             if(!suppressEvent){
18543                 this.fireEvent("selectionchange", this, this.selections);
18544             }
18545         }
18546     },
18547
18548     /**
18549      * Returns true if the passed node is selected
18550      * @param {HTMLElement/Number} node The node or node index
18551      * @return {Boolean}
18552      */
18553     isSelected : function(node){
18554         var s = this.selections;
18555         if(s.length < 1){
18556             return false;
18557         }
18558         node = this.getNode(node);
18559         return s.indexOf(node) !== -1;
18560     },
18561
18562     /**
18563      * Selects nodes.
18564      * @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
18565      * @param {Boolean} keepExisting (optional) true to keep existing selections
18566      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18567      */
18568     select : function(nodeInfo, keepExisting, suppressEvent){
18569         if(nodeInfo instanceof Array){
18570             if(!keepExisting){
18571                 this.clearSelections(true);
18572             }
18573             for(var i = 0, len = nodeInfo.length; i < len; i++){
18574                 this.select(nodeInfo[i], true, true);
18575             }
18576             return;
18577         } 
18578         var node = this.getNode(nodeInfo);
18579         if(!node || this.isSelected(node)){
18580             return; // already selected.
18581         }
18582         if(!keepExisting){
18583             this.clearSelections(true);
18584         }
18585         
18586         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18587             Roo.fly(node).addClass(this.selectedClass);
18588             this.selections.push(node);
18589             if(!suppressEvent){
18590                 this.fireEvent("selectionchange", this, this.selections);
18591             }
18592         }
18593         
18594         
18595     },
18596       /**
18597      * Unselects nodes.
18598      * @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
18599      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18600      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18601      */
18602     unselect : function(nodeInfo, keepExisting, suppressEvent)
18603     {
18604         if(nodeInfo instanceof Array){
18605             Roo.each(this.selections, function(s) {
18606                 this.unselect(s, nodeInfo);
18607             }, this);
18608             return;
18609         }
18610         var node = this.getNode(nodeInfo);
18611         if(!node || !this.isSelected(node)){
18612             //Roo.log("not selected");
18613             return; // not selected.
18614         }
18615         // fireevent???
18616         var ns = [];
18617         Roo.each(this.selections, function(s) {
18618             if (s == node ) {
18619                 Roo.fly(node).removeClass(this.selectedClass);
18620
18621                 return;
18622             }
18623             ns.push(s);
18624         },this);
18625         
18626         this.selections= ns;
18627         this.fireEvent("selectionchange", this, this.selections);
18628     },
18629
18630     /**
18631      * Gets a template node.
18632      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18633      * @return {HTMLElement} The node or null if it wasn't found
18634      */
18635     getNode : function(nodeInfo){
18636         if(typeof nodeInfo == "string"){
18637             return document.getElementById(nodeInfo);
18638         }else if(typeof nodeInfo == "number"){
18639             return this.nodes[nodeInfo];
18640         }
18641         return nodeInfo;
18642     },
18643
18644     /**
18645      * Gets a range template nodes.
18646      * @param {Number} startIndex
18647      * @param {Number} endIndex
18648      * @return {Array} An array of nodes
18649      */
18650     getNodes : function(start, end){
18651         var ns = this.nodes;
18652         start = start || 0;
18653         end = typeof end == "undefined" ? ns.length - 1 : end;
18654         var nodes = [];
18655         if(start <= end){
18656             for(var i = start; i <= end; i++){
18657                 nodes.push(ns[i]);
18658             }
18659         } else{
18660             for(var i = start; i >= end; i--){
18661                 nodes.push(ns[i]);
18662             }
18663         }
18664         return nodes;
18665     },
18666
18667     /**
18668      * Finds the index of the passed node
18669      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18670      * @return {Number} The index of the node or -1
18671      */
18672     indexOf : function(node){
18673         node = this.getNode(node);
18674         if(typeof node.nodeIndex == "number"){
18675             return node.nodeIndex;
18676         }
18677         var ns = this.nodes;
18678         for(var i = 0, len = ns.length; i < len; i++){
18679             if(ns[i] == node){
18680                 return i;
18681             }
18682         }
18683         return -1;
18684     }
18685 });
18686 /*
18687  * - LGPL
18688  *
18689  * based on jquery fullcalendar
18690  * 
18691  */
18692
18693 Roo.bootstrap = Roo.bootstrap || {};
18694 /**
18695  * @class Roo.bootstrap.Calendar
18696  * @extends Roo.bootstrap.Component
18697  * Bootstrap Calendar class
18698  * @cfg {Boolean} loadMask (true|false) default false
18699  * @cfg {Object} header generate the user specific header of the calendar, default false
18700
18701  * @constructor
18702  * Create a new Container
18703  * @param {Object} config The config object
18704  */
18705
18706
18707
18708 Roo.bootstrap.Calendar = function(config){
18709     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18710      this.addEvents({
18711         /**
18712              * @event select
18713              * Fires when a date is selected
18714              * @param {DatePicker} this
18715              * @param {Date} date The selected date
18716              */
18717         'select': true,
18718         /**
18719              * @event monthchange
18720              * Fires when the displayed month changes 
18721              * @param {DatePicker} this
18722              * @param {Date} date The selected month
18723              */
18724         'monthchange': true,
18725         /**
18726              * @event evententer
18727              * Fires when mouse over an event
18728              * @param {Calendar} this
18729              * @param {event} Event
18730              */
18731         'evententer': true,
18732         /**
18733              * @event eventleave
18734              * Fires when the mouse leaves an
18735              * @param {Calendar} this
18736              * @param {event}
18737              */
18738         'eventleave': true,
18739         /**
18740              * @event eventclick
18741              * Fires when the mouse click an
18742              * @param {Calendar} this
18743              * @param {event}
18744              */
18745         'eventclick': true
18746         
18747     });
18748
18749 };
18750
18751 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18752     
18753      /**
18754      * @cfg {Number} startDay
18755      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18756      */
18757     startDay : 0,
18758     
18759     loadMask : false,
18760     
18761     header : false,
18762       
18763     getAutoCreate : function(){
18764         
18765         
18766         var fc_button = function(name, corner, style, content ) {
18767             return Roo.apply({},{
18768                 tag : 'span',
18769                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18770                          (corner.length ?
18771                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18772                             ''
18773                         ),
18774                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18775                 unselectable: 'on'
18776             });
18777         };
18778         
18779         var header = {};
18780         
18781         if(!this.header){
18782             header = {
18783                 tag : 'table',
18784                 cls : 'fc-header',
18785                 style : 'width:100%',
18786                 cn : [
18787                     {
18788                         tag: 'tr',
18789                         cn : [
18790                             {
18791                                 tag : 'td',
18792                                 cls : 'fc-header-left',
18793                                 cn : [
18794                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18795                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18796                                     { tag: 'span', cls: 'fc-header-space' },
18797                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18798
18799
18800                                 ]
18801                             },
18802
18803                             {
18804                                 tag : 'td',
18805                                 cls : 'fc-header-center',
18806                                 cn : [
18807                                     {
18808                                         tag: 'span',
18809                                         cls: 'fc-header-title',
18810                                         cn : {
18811                                             tag: 'H2',
18812                                             html : 'month / year'
18813                                         }
18814                                     }
18815
18816                                 ]
18817                             },
18818                             {
18819                                 tag : 'td',
18820                                 cls : 'fc-header-right',
18821                                 cn : [
18822                               /*      fc_button('month', 'left', '', 'month' ),
18823                                     fc_button('week', '', '', 'week' ),
18824                                     fc_button('day', 'right', '', 'day' )
18825                                 */    
18826
18827                                 ]
18828                             }
18829
18830                         ]
18831                     }
18832                 ]
18833             };
18834         }
18835         
18836         header = this.header;
18837         
18838        
18839         var cal_heads = function() {
18840             var ret = [];
18841             // fixme - handle this.
18842             
18843             for (var i =0; i < Date.dayNames.length; i++) {
18844                 var d = Date.dayNames[i];
18845                 ret.push({
18846                     tag: 'th',
18847                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18848                     html : d.substring(0,3)
18849                 });
18850                 
18851             }
18852             ret[0].cls += ' fc-first';
18853             ret[6].cls += ' fc-last';
18854             return ret;
18855         };
18856         var cal_cell = function(n) {
18857             return  {
18858                 tag: 'td',
18859                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18860                 cn : [
18861                     {
18862                         cn : [
18863                             {
18864                                 cls: 'fc-day-number',
18865                                 html: 'D'
18866                             },
18867                             {
18868                                 cls: 'fc-day-content',
18869                              
18870                                 cn : [
18871                                      {
18872                                         style: 'position: relative;' // height: 17px;
18873                                     }
18874                                 ]
18875                             }
18876                             
18877                             
18878                         ]
18879                     }
18880                 ]
18881                 
18882             }
18883         };
18884         var cal_rows = function() {
18885             
18886             var ret = [];
18887             for (var r = 0; r < 6; r++) {
18888                 var row= {
18889                     tag : 'tr',
18890                     cls : 'fc-week',
18891                     cn : []
18892                 };
18893                 
18894                 for (var i =0; i < Date.dayNames.length; i++) {
18895                     var d = Date.dayNames[i];
18896                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18897
18898                 }
18899                 row.cn[0].cls+=' fc-first';
18900                 row.cn[0].cn[0].style = 'min-height:90px';
18901                 row.cn[6].cls+=' fc-last';
18902                 ret.push(row);
18903                 
18904             }
18905             ret[0].cls += ' fc-first';
18906             ret[4].cls += ' fc-prev-last';
18907             ret[5].cls += ' fc-last';
18908             return ret;
18909             
18910         };
18911         
18912         var cal_table = {
18913             tag: 'table',
18914             cls: 'fc-border-separate',
18915             style : 'width:100%',
18916             cellspacing  : 0,
18917             cn : [
18918                 { 
18919                     tag: 'thead',
18920                     cn : [
18921                         { 
18922                             tag: 'tr',
18923                             cls : 'fc-first fc-last',
18924                             cn : cal_heads()
18925                         }
18926                     ]
18927                 },
18928                 { 
18929                     tag: 'tbody',
18930                     cn : cal_rows()
18931                 }
18932                   
18933             ]
18934         };
18935          
18936          var cfg = {
18937             cls : 'fc fc-ltr',
18938             cn : [
18939                 header,
18940                 {
18941                     cls : 'fc-content',
18942                     style : "position: relative;",
18943                     cn : [
18944                         {
18945                             cls : 'fc-view fc-view-month fc-grid',
18946                             style : 'position: relative',
18947                             unselectable : 'on',
18948                             cn : [
18949                                 {
18950                                     cls : 'fc-event-container',
18951                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18952                                 },
18953                                 cal_table
18954                             ]
18955                         }
18956                     ]
18957     
18958                 }
18959            ] 
18960             
18961         };
18962         
18963          
18964         
18965         return cfg;
18966     },
18967     
18968     
18969     initEvents : function()
18970     {
18971         if(!this.store){
18972             throw "can not find store for calendar";
18973         }
18974         
18975         var mark = {
18976             tag: "div",
18977             cls:"x-dlg-mask",
18978             style: "text-align:center",
18979             cn: [
18980                 {
18981                     tag: "div",
18982                     style: "background-color:white;width:50%;margin:250 auto",
18983                     cn: [
18984                         {
18985                             tag: "img",
18986                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18987                         },
18988                         {
18989                             tag: "span",
18990                             html: "Loading"
18991                         }
18992                         
18993                     ]
18994                 }
18995             ]
18996         };
18997         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18998         
18999         var size = this.el.select('.fc-content', true).first().getSize();
19000         this.maskEl.setSize(size.width, size.height);
19001         this.maskEl.enableDisplayMode("block");
19002         if(!this.loadMask){
19003             this.maskEl.hide();
19004         }
19005         
19006         this.store = Roo.factory(this.store, Roo.data);
19007         this.store.on('load', this.onLoad, this);
19008         this.store.on('beforeload', this.onBeforeLoad, this);
19009         
19010         this.resize();
19011         
19012         this.cells = this.el.select('.fc-day',true);
19013         //Roo.log(this.cells);
19014         this.textNodes = this.el.query('.fc-day-number');
19015         this.cells.addClassOnOver('fc-state-hover');
19016         
19017         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19018         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19019         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19020         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19021         
19022         this.on('monthchange', this.onMonthChange, this);
19023         
19024         this.update(new Date().clearTime());
19025     },
19026     
19027     resize : function() {
19028         var sz  = this.el.getSize();
19029         
19030         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19031         this.el.select('.fc-day-content div',true).setHeight(34);
19032     },
19033     
19034     
19035     // private
19036     showPrevMonth : function(e){
19037         this.update(this.activeDate.add("mo", -1));
19038     },
19039     showToday : function(e){
19040         this.update(new Date().clearTime());
19041     },
19042     // private
19043     showNextMonth : function(e){
19044         this.update(this.activeDate.add("mo", 1));
19045     },
19046
19047     // private
19048     showPrevYear : function(){
19049         this.update(this.activeDate.add("y", -1));
19050     },
19051
19052     // private
19053     showNextYear : function(){
19054         this.update(this.activeDate.add("y", 1));
19055     },
19056
19057     
19058    // private
19059     update : function(date)
19060     {
19061         var vd = this.activeDate;
19062         this.activeDate = date;
19063 //        if(vd && this.el){
19064 //            var t = date.getTime();
19065 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19066 //                Roo.log('using add remove');
19067 //                
19068 //                this.fireEvent('monthchange', this, date);
19069 //                
19070 //                this.cells.removeClass("fc-state-highlight");
19071 //                this.cells.each(function(c){
19072 //                   if(c.dateValue == t){
19073 //                       c.addClass("fc-state-highlight");
19074 //                       setTimeout(function(){
19075 //                            try{c.dom.firstChild.focus();}catch(e){}
19076 //                       }, 50);
19077 //                       return false;
19078 //                   }
19079 //                   return true;
19080 //                });
19081 //                return;
19082 //            }
19083 //        }
19084         
19085         var days = date.getDaysInMonth();
19086         
19087         var firstOfMonth = date.getFirstDateOfMonth();
19088         var startingPos = firstOfMonth.getDay()-this.startDay;
19089         
19090         if(startingPos < this.startDay){
19091             startingPos += 7;
19092         }
19093         
19094         var pm = date.add(Date.MONTH, -1);
19095         var prevStart = pm.getDaysInMonth()-startingPos;
19096 //        
19097         this.cells = this.el.select('.fc-day',true);
19098         this.textNodes = this.el.query('.fc-day-number');
19099         this.cells.addClassOnOver('fc-state-hover');
19100         
19101         var cells = this.cells.elements;
19102         var textEls = this.textNodes;
19103         
19104         Roo.each(cells, function(cell){
19105             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19106         });
19107         
19108         days += startingPos;
19109
19110         // convert everything to numbers so it's fast
19111         var day = 86400000;
19112         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19113         //Roo.log(d);
19114         //Roo.log(pm);
19115         //Roo.log(prevStart);
19116         
19117         var today = new Date().clearTime().getTime();
19118         var sel = date.clearTime().getTime();
19119         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19120         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19121         var ddMatch = this.disabledDatesRE;
19122         var ddText = this.disabledDatesText;
19123         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19124         var ddaysText = this.disabledDaysText;
19125         var format = this.format;
19126         
19127         var setCellClass = function(cal, cell){
19128             cell.row = 0;
19129             cell.events = [];
19130             cell.more = [];
19131             //Roo.log('set Cell Class');
19132             cell.title = "";
19133             var t = d.getTime();
19134             
19135             //Roo.log(d);
19136             
19137             cell.dateValue = t;
19138             if(t == today){
19139                 cell.className += " fc-today";
19140                 cell.className += " fc-state-highlight";
19141                 cell.title = cal.todayText;
19142             }
19143             if(t == sel){
19144                 // disable highlight in other month..
19145                 //cell.className += " fc-state-highlight";
19146                 
19147             }
19148             // disabling
19149             if(t < min) {
19150                 cell.className = " fc-state-disabled";
19151                 cell.title = cal.minText;
19152                 return;
19153             }
19154             if(t > max) {
19155                 cell.className = " fc-state-disabled";
19156                 cell.title = cal.maxText;
19157                 return;
19158             }
19159             if(ddays){
19160                 if(ddays.indexOf(d.getDay()) != -1){
19161                     cell.title = ddaysText;
19162                     cell.className = " fc-state-disabled";
19163                 }
19164             }
19165             if(ddMatch && format){
19166                 var fvalue = d.dateFormat(format);
19167                 if(ddMatch.test(fvalue)){
19168                     cell.title = ddText.replace("%0", fvalue);
19169                     cell.className = " fc-state-disabled";
19170                 }
19171             }
19172             
19173             if (!cell.initialClassName) {
19174                 cell.initialClassName = cell.dom.className;
19175             }
19176             
19177             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19178         };
19179
19180         var i = 0;
19181         
19182         for(; i < startingPos; i++) {
19183             textEls[i].innerHTML = (++prevStart);
19184             d.setDate(d.getDate()+1);
19185             
19186             cells[i].className = "fc-past fc-other-month";
19187             setCellClass(this, cells[i]);
19188         }
19189         
19190         var intDay = 0;
19191         
19192         for(; i < days; i++){
19193             intDay = i - startingPos + 1;
19194             textEls[i].innerHTML = (intDay);
19195             d.setDate(d.getDate()+1);
19196             
19197             cells[i].className = ''; // "x-date-active";
19198             setCellClass(this, cells[i]);
19199         }
19200         var extraDays = 0;
19201         
19202         for(; i < 42; i++) {
19203             textEls[i].innerHTML = (++extraDays);
19204             d.setDate(d.getDate()+1);
19205             
19206             cells[i].className = "fc-future fc-other-month";
19207             setCellClass(this, cells[i]);
19208         }
19209         
19210         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19211         
19212         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19213         
19214         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19215         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19216         
19217         if(totalRows != 6){
19218             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19219             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19220         }
19221         
19222         this.fireEvent('monthchange', this, date);
19223         
19224         
19225         /*
19226         if(!this.internalRender){
19227             var main = this.el.dom.firstChild;
19228             var w = main.offsetWidth;
19229             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19230             Roo.fly(main).setWidth(w);
19231             this.internalRender = true;
19232             // opera does not respect the auto grow header center column
19233             // then, after it gets a width opera refuses to recalculate
19234             // without a second pass
19235             if(Roo.isOpera && !this.secondPass){
19236                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19237                 this.secondPass = true;
19238                 this.update.defer(10, this, [date]);
19239             }
19240         }
19241         */
19242         
19243     },
19244     
19245     findCell : function(dt) {
19246         dt = dt.clearTime().getTime();
19247         var ret = false;
19248         this.cells.each(function(c){
19249             //Roo.log("check " +c.dateValue + '?=' + dt);
19250             if(c.dateValue == dt){
19251                 ret = c;
19252                 return false;
19253             }
19254             return true;
19255         });
19256         
19257         return ret;
19258     },
19259     
19260     findCells : function(ev) {
19261         var s = ev.start.clone().clearTime().getTime();
19262        // Roo.log(s);
19263         var e= ev.end.clone().clearTime().getTime();
19264        // Roo.log(e);
19265         var ret = [];
19266         this.cells.each(function(c){
19267              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19268             
19269             if(c.dateValue > e){
19270                 return ;
19271             }
19272             if(c.dateValue < s){
19273                 return ;
19274             }
19275             ret.push(c);
19276         });
19277         
19278         return ret;    
19279     },
19280     
19281 //    findBestRow: function(cells)
19282 //    {
19283 //        var ret = 0;
19284 //        
19285 //        for (var i =0 ; i < cells.length;i++) {
19286 //            ret  = Math.max(cells[i].rows || 0,ret);
19287 //        }
19288 //        return ret;
19289 //        
19290 //    },
19291     
19292     
19293     addItem : function(ev)
19294     {
19295         // look for vertical location slot in
19296         var cells = this.findCells(ev);
19297         
19298 //        ev.row = this.findBestRow(cells);
19299         
19300         // work out the location.
19301         
19302         var crow = false;
19303         var rows = [];
19304         for(var i =0; i < cells.length; i++) {
19305             
19306             cells[i].row = cells[0].row;
19307             
19308             if(i == 0){
19309                 cells[i].row = cells[i].row + 1;
19310             }
19311             
19312             if (!crow) {
19313                 crow = {
19314                     start : cells[i],
19315                     end :  cells[i]
19316                 };
19317                 continue;
19318             }
19319             if (crow.start.getY() == cells[i].getY()) {
19320                 // on same row.
19321                 crow.end = cells[i];
19322                 continue;
19323             }
19324             // different row.
19325             rows.push(crow);
19326             crow = {
19327                 start: cells[i],
19328                 end : cells[i]
19329             };
19330             
19331         }
19332         
19333         rows.push(crow);
19334         ev.els = [];
19335         ev.rows = rows;
19336         ev.cells = cells;
19337         
19338         cells[0].events.push(ev);
19339         
19340         this.calevents.push(ev);
19341     },
19342     
19343     clearEvents: function() {
19344         
19345         if(!this.calevents){
19346             return;
19347         }
19348         
19349         Roo.each(this.cells.elements, function(c){
19350             c.row = 0;
19351             c.events = [];
19352             c.more = [];
19353         });
19354         
19355         Roo.each(this.calevents, function(e) {
19356             Roo.each(e.els, function(el) {
19357                 el.un('mouseenter' ,this.onEventEnter, this);
19358                 el.un('mouseleave' ,this.onEventLeave, this);
19359                 el.remove();
19360             },this);
19361         },this);
19362         
19363         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19364             e.remove();
19365         });
19366         
19367     },
19368     
19369     renderEvents: function()
19370     {   
19371         var _this = this;
19372         
19373         this.cells.each(function(c) {
19374             
19375             if(c.row < 5){
19376                 return;
19377             }
19378             
19379             var ev = c.events;
19380             
19381             var r = 4;
19382             if(c.row != c.events.length){
19383                 r = 4 - (4 - (c.row - c.events.length));
19384             }
19385             
19386             c.events = ev.slice(0, r);
19387             c.more = ev.slice(r);
19388             
19389             if(c.more.length && c.more.length == 1){
19390                 c.events.push(c.more.pop());
19391             }
19392             
19393             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19394             
19395         });
19396             
19397         this.cells.each(function(c) {
19398             
19399             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19400             
19401             
19402             for (var e = 0; e < c.events.length; e++){
19403                 var ev = c.events[e];
19404                 var rows = ev.rows;
19405                 
19406                 for(var i = 0; i < rows.length; i++) {
19407                 
19408                     // how many rows should it span..
19409
19410                     var  cfg = {
19411                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19412                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19413
19414                         unselectable : "on",
19415                         cn : [
19416                             {
19417                                 cls: 'fc-event-inner',
19418                                 cn : [
19419     //                                {
19420     //                                  tag:'span',
19421     //                                  cls: 'fc-event-time',
19422     //                                  html : cells.length > 1 ? '' : ev.time
19423     //                                },
19424                                     {
19425                                       tag:'span',
19426                                       cls: 'fc-event-title',
19427                                       html : String.format('{0}', ev.title)
19428                                     }
19429
19430
19431                                 ]
19432                             },
19433                             {
19434                                 cls: 'ui-resizable-handle ui-resizable-e',
19435                                 html : '&nbsp;&nbsp;&nbsp'
19436                             }
19437
19438                         ]
19439                     };
19440
19441                     if (i == 0) {
19442                         cfg.cls += ' fc-event-start';
19443                     }
19444                     if ((i+1) == rows.length) {
19445                         cfg.cls += ' fc-event-end';
19446                     }
19447
19448                     var ctr = _this.el.select('.fc-event-container',true).first();
19449                     var cg = ctr.createChild(cfg);
19450
19451                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19452                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19453
19454                     var r = (c.more.length) ? 1 : 0;
19455                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19456                     cg.setWidth(ebox.right - sbox.x -2);
19457
19458                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19459                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19460                     cg.on('click', _this.onEventClick, _this, ev);
19461
19462                     ev.els.push(cg);
19463                     
19464                 }
19465                 
19466             }
19467             
19468             
19469             if(c.more.length){
19470                 var  cfg = {
19471                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19472                     style : 'position: absolute',
19473                     unselectable : "on",
19474                     cn : [
19475                         {
19476                             cls: 'fc-event-inner',
19477                             cn : [
19478                                 {
19479                                   tag:'span',
19480                                   cls: 'fc-event-title',
19481                                   html : 'More'
19482                                 }
19483
19484
19485                             ]
19486                         },
19487                         {
19488                             cls: 'ui-resizable-handle ui-resizable-e',
19489                             html : '&nbsp;&nbsp;&nbsp'
19490                         }
19491
19492                     ]
19493                 };
19494
19495                 var ctr = _this.el.select('.fc-event-container',true).first();
19496                 var cg = ctr.createChild(cfg);
19497
19498                 var sbox = c.select('.fc-day-content',true).first().getBox();
19499                 var ebox = c.select('.fc-day-content',true).first().getBox();
19500                 //Roo.log(cg);
19501                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19502                 cg.setWidth(ebox.right - sbox.x -2);
19503
19504                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19505                 
19506             }
19507             
19508         });
19509         
19510         
19511         
19512     },
19513     
19514     onEventEnter: function (e, el,event,d) {
19515         this.fireEvent('evententer', this, el, event);
19516     },
19517     
19518     onEventLeave: function (e, el,event,d) {
19519         this.fireEvent('eventleave', this, el, event);
19520     },
19521     
19522     onEventClick: function (e, el,event,d) {
19523         this.fireEvent('eventclick', this, el, event);
19524     },
19525     
19526     onMonthChange: function () {
19527         this.store.load();
19528     },
19529     
19530     onMoreEventClick: function(e, el, more)
19531     {
19532         var _this = this;
19533         
19534         this.calpopover.placement = 'right';
19535         this.calpopover.setTitle('More');
19536         
19537         this.calpopover.setContent('');
19538         
19539         var ctr = this.calpopover.el.select('.popover-content', true).first();
19540         
19541         Roo.each(more, function(m){
19542             var cfg = {
19543                 cls : 'fc-event-hori fc-event-draggable',
19544                 html : m.title
19545             };
19546             var cg = ctr.createChild(cfg);
19547             
19548             cg.on('click', _this.onEventClick, _this, m);
19549         });
19550         
19551         this.calpopover.show(el);
19552         
19553         
19554     },
19555     
19556     onLoad: function () 
19557     {   
19558         this.calevents = [];
19559         var cal = this;
19560         
19561         if(this.store.getCount() > 0){
19562             this.store.data.each(function(d){
19563                cal.addItem({
19564                     id : d.data.id,
19565                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19566                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19567                     time : d.data.start_time,
19568                     title : d.data.title,
19569                     description : d.data.description,
19570                     venue : d.data.venue
19571                 });
19572             });
19573         }
19574         
19575         this.renderEvents();
19576         
19577         if(this.calevents.length && this.loadMask){
19578             this.maskEl.hide();
19579         }
19580     },
19581     
19582     onBeforeLoad: function()
19583     {
19584         this.clearEvents();
19585         if(this.loadMask){
19586             this.maskEl.show();
19587         }
19588     }
19589 });
19590
19591  
19592  /*
19593  * - LGPL
19594  *
19595  * element
19596  * 
19597  */
19598
19599 /**
19600  * @class Roo.bootstrap.Popover
19601  * @extends Roo.bootstrap.Component
19602  * Bootstrap Popover class
19603  * @cfg {String} html contents of the popover   (or false to use children..)
19604  * @cfg {String} title of popover (or false to hide)
19605  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19606  * @cfg {String} trigger click || hover (or false to trigger manually)
19607  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19608  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19609  *      - if false and it has a 'parent' then it will be automatically added to that element
19610  *      - if string - Roo.get  will be called 
19611  * @cfg {Number} delay - delay before showing
19612  
19613  * @constructor
19614  * Create a new Popover
19615  * @param {Object} config The config object
19616  */
19617
19618 Roo.bootstrap.Popover = function(config){
19619     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19620     
19621     this.addEvents({
19622         // raw events
19623          /**
19624          * @event show
19625          * After the popover show
19626          * 
19627          * @param {Roo.bootstrap.Popover} this
19628          */
19629         "show" : true,
19630         /**
19631          * @event hide
19632          * After the popover hide
19633          * 
19634          * @param {Roo.bootstrap.Popover} this
19635          */
19636         "hide" : true
19637     });
19638 };
19639
19640 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19641     
19642     title: false,
19643     html: false,
19644     
19645     placement : 'right',
19646     trigger : 'hover', // hover
19647     modal : false,
19648     delay : 0,
19649     
19650     over: false,
19651     
19652     can_build_overlaid : false,
19653     
19654     maskEl : false, // the mask element
19655     headerEl : false,
19656     contentEl : false,
19657     
19658     
19659     getChildContainer : function()
19660     {
19661         return this.contentEl;
19662         
19663     },
19664     getPopoverHeader : function()
19665     {
19666         this.title = true; // flag not to hide it..
19667         return this.headerEl
19668     },
19669     
19670     
19671     getAutoCreate : function(){
19672          
19673         var cfg = {
19674            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19675            style: 'display:block',
19676            cn : [
19677                 {
19678                     cls : 'arrow'
19679                 },
19680                 {
19681                     cls : 'popover-inner ',
19682                     cn : [
19683                         {
19684                             tag: 'h3',
19685                             cls: 'popover-title popover-header',
19686                             html : this.title === false ? '' : this.title
19687                         },
19688                         {
19689                             cls : 'popover-content popover-body'  + this.cls,
19690                             html : this.html || ''
19691                         }
19692                     ]
19693                     
19694                 }
19695            ]
19696         };
19697         
19698         return cfg;
19699     },
19700     /**
19701      * @param {string} the title
19702      */
19703     setTitle: function(str)
19704     {
19705         this.title = str;
19706         if (this.el) {
19707             this.headerEl.dom.innerHTML = str;
19708         }
19709         
19710     },
19711     /**
19712      * @param {string} the body content
19713      */
19714     setContent: function(str)
19715     {
19716         this.html = str;
19717         if (this.contentEl) {
19718             this.contentEl.dom.innerHTML = str;
19719         }
19720         
19721     },
19722     // as it get's added to the bottom of the page.
19723     onRender : function(ct, position)
19724     {
19725         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19726         if(!this.el){
19727             var cfg = Roo.apply({},  this.getAutoCreate());
19728             cfg.id = Roo.id();
19729             
19730             if (this.cls) {
19731                 cfg.cls += ' ' + this.cls;
19732             }
19733             if (this.style) {
19734                 cfg.style = this.style;
19735             }
19736             //Roo.log("adding to ");
19737             this.el = Roo.get(document.body).createChild(cfg, position);
19738 //            Roo.log(this.el);
19739         }
19740         
19741         var nitems = [];
19742         if(typeof(this.items) != 'undefined'){
19743             var items = this.items;
19744             delete this.items;
19745
19746             for(var i =0;i < items.length;i++) {
19747                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19748             }
19749         }
19750
19751         this.items = nitems;
19752         
19753         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19754         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19755         
19756         
19757         this.contentEl = this.el.select('.popover-content',true).first();
19758         this.headerEl =  this.el.select('.popover-header',true).first();
19759         
19760         this.initEvents();
19761     },
19762     
19763     resizeMask : function()
19764     {
19765         this.maskEl.setSize(
19766             Roo.lib.Dom.getViewWidth(true),
19767             Roo.lib.Dom.getViewHeight(true)
19768         );
19769     },
19770     
19771     initEvents : function()
19772     {
19773         
19774         if (!this.modal) { 
19775             Roo.bootstrap.Popover.register(this);
19776         }
19777          
19778         
19779         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19780         this.el.enableDisplayMode('block');
19781         this.el.hide();
19782         if (this.over === false && !this.parent()) {
19783             return; 
19784         }
19785         if (this.triggers === false) {
19786             return;
19787         }
19788          
19789         // support parent
19790         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19791         var triggers = this.trigger ? this.trigger.split(' ') : [];
19792         Roo.each(triggers, function(trigger) {
19793         
19794             if (trigger == 'click') {
19795                 on_el.on('click', this.toggle, this);
19796             } else if (trigger != 'manual') {
19797                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19798                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19799       
19800                 on_el.on(eventIn  ,this.enter, this);
19801                 on_el.on(eventOut, this.leave, this);
19802             }
19803         }, this);
19804         
19805     },
19806     
19807     
19808     // private
19809     timeout : null,
19810     hoverState : null,
19811     
19812     toggle : function () {
19813         this.hoverState == 'in' ? this.leave() : this.enter();
19814     },
19815     
19816     enter : function () {
19817         
19818         clearTimeout(this.timeout);
19819     
19820         this.hoverState = 'in';
19821     
19822         if (!this.delay || !this.delay.show) {
19823             this.show();
19824             return;
19825         }
19826         var _t = this;
19827         this.timeout = setTimeout(function () {
19828             if (_t.hoverState == 'in') {
19829                 _t.show();
19830             }
19831         }, this.delay.show)
19832     },
19833     
19834     leave : function() {
19835         clearTimeout(this.timeout);
19836     
19837         this.hoverState = 'out';
19838     
19839         if (!this.delay || !this.delay.hide) {
19840             this.hide();
19841             return;
19842         }
19843         var _t = this;
19844         this.timeout = setTimeout(function () {
19845             if (_t.hoverState == 'out') {
19846                 _t.hide();
19847             }
19848         }, this.delay.hide)
19849     },
19850     /**
19851      * Show the popover
19852      * @param {Roo.Element|string|false} - element to align and point to.
19853      */
19854     show : function (on_el)
19855     {
19856         
19857         on_el = on_el || false; // default to false
19858         if (!on_el) {
19859             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19860                 on_el = this.parent().el;
19861             } else if (this.over) {
19862                 Roo.get(this.over);
19863             }
19864             
19865         }
19866         
19867         if (!this.el) {
19868             this.render(document.body);
19869         }
19870         
19871         
19872         this.el.removeClass([
19873             'fade','top','bottom', 'left', 'right','in',
19874             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19875         ]);
19876         
19877         if (this.title === false) {
19878             this.headerEl.hide();
19879         }
19880         
19881         
19882         var placement = typeof this.placement == 'function' ?
19883             this.placement.call(this, this.el, on_el) :
19884             this.placement;
19885             
19886         /*
19887         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19888         
19889         // I think  'auto right' - but 
19890         
19891         var autoPlace = autoToken.test(placement);
19892         if (autoPlace) {
19893             placement = placement.replace(autoToken, '') || 'top';
19894         }
19895         */
19896         
19897         
19898         this.el.show();
19899         this.el.dom.style.display='block';
19900         
19901         //this.el.appendTo(on_el);
19902         
19903         var p = this.getPosition();
19904         var box = this.el.getBox();
19905         
19906         
19907         var align = Roo.bootstrap.Popover.alignment[placement];
19908         this.el.addClass(align[2]);
19909
19910 //        Roo.log(align);
19911
19912         if (on_el) {
19913             this.el.alignTo(on_el, align[0],align[1]);
19914         } else {
19915             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19916             var es = this.el.getSize();
19917             var x = Roo.lib.Dom.getViewWidth()/2;
19918             var y = Roo.lib.Dom.getViewHeight()/2;
19919             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19920             
19921         }
19922
19923         
19924         //var arrow = this.el.select('.arrow',true).first();
19925         //arrow.set(align[2], 
19926         
19927         this.el.addClass('in');
19928         
19929         
19930         if (this.el.hasClass('fade')) {
19931             // fade it?
19932         }
19933         
19934         this.hoverState = 'in';
19935         
19936         if (this.modal) {
19937             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19938             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19939             this.maskEl.dom.style.display = 'block';
19940             this.maskEl.addClass('show');
19941         }
19942         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19943
19944         
19945         
19946         this.fireEvent('show', this);
19947         
19948     },
19949     hide : function()
19950     {
19951         this.el.setXY([0,0]);
19952         this.el.removeClass('in');
19953         this.el.hide();
19954         this.hoverState = null;
19955         this.maskEl.hide(); // always..
19956         this.fireEvent('hide', this);
19957     }
19958     
19959 });
19960
19961
19962 Roo.apply(Roo.bootstrap.Popover, {
19963
19964     alignment : {
19965         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19966         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19967         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19968         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19969     },
19970     
19971     zIndex : 20001,
19972
19973     clickHander : false,
19974     
19975
19976     onMouseDown : function(e)
19977     {
19978         if (!e.getTarget(".roo-popover")) {
19979             this.hideAll();
19980         }
19981          
19982     },
19983     
19984     popups : [],
19985     
19986     register : function(popup)
19987     {
19988         if (!Roo.bootstrap.Popover.clickHandler) {
19989             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19990         }
19991         // hide other popups.
19992         this.hideAll();
19993         this.popups.push(popup);
19994     },
19995     hideAll : function()
19996     {
19997         this.popups.forEach(function(p) {
19998             p.hide();
19999         });
20000     }
20001
20002 });/*
20003  * - LGPL
20004  *
20005  * Card header - holder for the card header elements.
20006  * 
20007  */
20008
20009 /**
20010  * @class Roo.bootstrap.PopoverNav
20011  * @extends Roo.bootstrap.NavGroup
20012  * Bootstrap Popover header navigation class
20013  * @constructor
20014  * Create a new Popover Header Navigation 
20015  * @param {Object} config The config object
20016  */
20017
20018 Roo.bootstrap.PopoverNav = function(config){
20019     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20020 };
20021
20022 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavGroup,  {
20023     
20024     
20025     container_method : 'getPopoverHeader' 
20026     
20027      
20028     
20029     
20030    
20031 });
20032
20033  
20034
20035  /*
20036  * - LGPL
20037  *
20038  * Progress
20039  * 
20040  */
20041
20042 /**
20043  * @class Roo.bootstrap.Progress
20044  * @extends Roo.bootstrap.Component
20045  * Bootstrap Progress class
20046  * @cfg {Boolean} striped striped of the progress bar
20047  * @cfg {Boolean} active animated of the progress bar
20048  * 
20049  * 
20050  * @constructor
20051  * Create a new Progress
20052  * @param {Object} config The config object
20053  */
20054
20055 Roo.bootstrap.Progress = function(config){
20056     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20057 };
20058
20059 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20060     
20061     striped : false,
20062     active: false,
20063     
20064     getAutoCreate : function(){
20065         var cfg = {
20066             tag: 'div',
20067             cls: 'progress'
20068         };
20069         
20070         
20071         if(this.striped){
20072             cfg.cls += ' progress-striped';
20073         }
20074       
20075         if(this.active){
20076             cfg.cls += ' active';
20077         }
20078         
20079         
20080         return cfg;
20081     }
20082    
20083 });
20084
20085  
20086
20087  /*
20088  * - LGPL
20089  *
20090  * ProgressBar
20091  * 
20092  */
20093
20094 /**
20095  * @class Roo.bootstrap.ProgressBar
20096  * @extends Roo.bootstrap.Component
20097  * Bootstrap ProgressBar class
20098  * @cfg {Number} aria_valuenow aria-value now
20099  * @cfg {Number} aria_valuemin aria-value min
20100  * @cfg {Number} aria_valuemax aria-value max
20101  * @cfg {String} label label for the progress bar
20102  * @cfg {String} panel (success | info | warning | danger )
20103  * @cfg {String} role role of the progress bar
20104  * @cfg {String} sr_only text
20105  * 
20106  * 
20107  * @constructor
20108  * Create a new ProgressBar
20109  * @param {Object} config The config object
20110  */
20111
20112 Roo.bootstrap.ProgressBar = function(config){
20113     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20114 };
20115
20116 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20117     
20118     aria_valuenow : 0,
20119     aria_valuemin : 0,
20120     aria_valuemax : 100,
20121     label : false,
20122     panel : false,
20123     role : false,
20124     sr_only: false,
20125     
20126     getAutoCreate : function()
20127     {
20128         
20129         var cfg = {
20130             tag: 'div',
20131             cls: 'progress-bar',
20132             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20133         };
20134         
20135         if(this.sr_only){
20136             cfg.cn = {
20137                 tag: 'span',
20138                 cls: 'sr-only',
20139                 html: this.sr_only
20140             }
20141         }
20142         
20143         if(this.role){
20144             cfg.role = this.role;
20145         }
20146         
20147         if(this.aria_valuenow){
20148             cfg['aria-valuenow'] = this.aria_valuenow;
20149         }
20150         
20151         if(this.aria_valuemin){
20152             cfg['aria-valuemin'] = this.aria_valuemin;
20153         }
20154         
20155         if(this.aria_valuemax){
20156             cfg['aria-valuemax'] = this.aria_valuemax;
20157         }
20158         
20159         if(this.label && !this.sr_only){
20160             cfg.html = this.label;
20161         }
20162         
20163         if(this.panel){
20164             cfg.cls += ' progress-bar-' + this.panel;
20165         }
20166         
20167         return cfg;
20168     },
20169     
20170     update : function(aria_valuenow)
20171     {
20172         this.aria_valuenow = aria_valuenow;
20173         
20174         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20175     }
20176    
20177 });
20178
20179  
20180
20181  /*
20182  * - LGPL
20183  *
20184  * column
20185  * 
20186  */
20187
20188 /**
20189  * @class Roo.bootstrap.TabGroup
20190  * @extends Roo.bootstrap.Column
20191  * Bootstrap Column class
20192  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20193  * @cfg {Boolean} carousel true to make the group behave like a carousel
20194  * @cfg {Boolean} bullets show bullets for the panels
20195  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20196  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20197  * @cfg {Boolean} showarrow (true|false) show arrow default true
20198  * 
20199  * @constructor
20200  * Create a new TabGroup
20201  * @param {Object} config The config object
20202  */
20203
20204 Roo.bootstrap.TabGroup = function(config){
20205     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20206     if (!this.navId) {
20207         this.navId = Roo.id();
20208     }
20209     this.tabs = [];
20210     Roo.bootstrap.TabGroup.register(this);
20211     
20212 };
20213
20214 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20215     
20216     carousel : false,
20217     transition : false,
20218     bullets : 0,
20219     timer : 0,
20220     autoslide : false,
20221     slideFn : false,
20222     slideOnTouch : false,
20223     showarrow : true,
20224     
20225     getAutoCreate : function()
20226     {
20227         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20228         
20229         cfg.cls += ' tab-content';
20230         
20231         if (this.carousel) {
20232             cfg.cls += ' carousel slide';
20233             
20234             cfg.cn = [{
20235                cls : 'carousel-inner',
20236                cn : []
20237             }];
20238         
20239             if(this.bullets  && !Roo.isTouch){
20240                 
20241                 var bullets = {
20242                     cls : 'carousel-bullets',
20243                     cn : []
20244                 };
20245                
20246                 if(this.bullets_cls){
20247                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20248                 }
20249                 
20250                 bullets.cn.push({
20251                     cls : 'clear'
20252                 });
20253                 
20254                 cfg.cn[0].cn.push(bullets);
20255             }
20256             
20257             if(this.showarrow){
20258                 cfg.cn[0].cn.push({
20259                     tag : 'div',
20260                     class : 'carousel-arrow',
20261                     cn : [
20262                         {
20263                             tag : 'div',
20264                             class : 'carousel-prev',
20265                             cn : [
20266                                 {
20267                                     tag : 'i',
20268                                     class : 'fa fa-chevron-left'
20269                                 }
20270                             ]
20271                         },
20272                         {
20273                             tag : 'div',
20274                             class : 'carousel-next',
20275                             cn : [
20276                                 {
20277                                     tag : 'i',
20278                                     class : 'fa fa-chevron-right'
20279                                 }
20280                             ]
20281                         }
20282                     ]
20283                 });
20284             }
20285             
20286         }
20287         
20288         return cfg;
20289     },
20290     
20291     initEvents:  function()
20292     {
20293 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20294 //            this.el.on("touchstart", this.onTouchStart, this);
20295 //        }
20296         
20297         if(this.autoslide){
20298             var _this = this;
20299             
20300             this.slideFn = window.setInterval(function() {
20301                 _this.showPanelNext();
20302             }, this.timer);
20303         }
20304         
20305         if(this.showarrow){
20306             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20307             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20308         }
20309         
20310         
20311     },
20312     
20313 //    onTouchStart : function(e, el, o)
20314 //    {
20315 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20316 //            return;
20317 //        }
20318 //        
20319 //        this.showPanelNext();
20320 //    },
20321     
20322     
20323     getChildContainer : function()
20324     {
20325         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20326     },
20327     
20328     /**
20329     * register a Navigation item
20330     * @param {Roo.bootstrap.NavItem} the navitem to add
20331     */
20332     register : function(item)
20333     {
20334         this.tabs.push( item);
20335         item.navId = this.navId; // not really needed..
20336         this.addBullet();
20337     
20338     },
20339     
20340     getActivePanel : function()
20341     {
20342         var r = false;
20343         Roo.each(this.tabs, function(t) {
20344             if (t.active) {
20345                 r = t;
20346                 return false;
20347             }
20348             return null;
20349         });
20350         return r;
20351         
20352     },
20353     getPanelByName : function(n)
20354     {
20355         var r = false;
20356         Roo.each(this.tabs, function(t) {
20357             if (t.tabId == n) {
20358                 r = t;
20359                 return false;
20360             }
20361             return null;
20362         });
20363         return r;
20364     },
20365     indexOfPanel : function(p)
20366     {
20367         var r = false;
20368         Roo.each(this.tabs, function(t,i) {
20369             if (t.tabId == p.tabId) {
20370                 r = i;
20371                 return false;
20372             }
20373             return null;
20374         });
20375         return r;
20376     },
20377     /**
20378      * show a specific panel
20379      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20380      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20381      */
20382     showPanel : function (pan)
20383     {
20384         if(this.transition || typeof(pan) == 'undefined'){
20385             Roo.log("waiting for the transitionend");
20386             return false;
20387         }
20388         
20389         if (typeof(pan) == 'number') {
20390             pan = this.tabs[pan];
20391         }
20392         
20393         if (typeof(pan) == 'string') {
20394             pan = this.getPanelByName(pan);
20395         }
20396         
20397         var cur = this.getActivePanel();
20398         
20399         if(!pan || !cur){
20400             Roo.log('pan or acitve pan is undefined');
20401             return false;
20402         }
20403         
20404         if (pan.tabId == this.getActivePanel().tabId) {
20405             return true;
20406         }
20407         
20408         if (false === cur.fireEvent('beforedeactivate')) {
20409             return false;
20410         }
20411         
20412         if(this.bullets > 0 && !Roo.isTouch){
20413             this.setActiveBullet(this.indexOfPanel(pan));
20414         }
20415         
20416         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20417             
20418             //class="carousel-item carousel-item-next carousel-item-left"
20419             
20420             this.transition = true;
20421             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20422             var lr = dir == 'next' ? 'left' : 'right';
20423             pan.el.addClass(dir); // or prev
20424             pan.el.addClass('carousel-item-' + dir); // or prev
20425             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20426             cur.el.addClass(lr); // or right
20427             pan.el.addClass(lr);
20428             cur.el.addClass('carousel-item-' +lr); // or right
20429             pan.el.addClass('carousel-item-' +lr);
20430             
20431             
20432             var _this = this;
20433             cur.el.on('transitionend', function() {
20434                 Roo.log("trans end?");
20435                 
20436                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20437                 pan.setActive(true);
20438                 
20439                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20440                 cur.setActive(false);
20441                 
20442                 _this.transition = false;
20443                 
20444             }, this, { single:  true } );
20445             
20446             return true;
20447         }
20448         
20449         cur.setActive(false);
20450         pan.setActive(true);
20451         
20452         return true;
20453         
20454     },
20455     showPanelNext : function()
20456     {
20457         var i = this.indexOfPanel(this.getActivePanel());
20458         
20459         if (i >= this.tabs.length - 1 && !this.autoslide) {
20460             return;
20461         }
20462         
20463         if (i >= this.tabs.length - 1 && this.autoslide) {
20464             i = -1;
20465         }
20466         
20467         this.showPanel(this.tabs[i+1]);
20468     },
20469     
20470     showPanelPrev : function()
20471     {
20472         var i = this.indexOfPanel(this.getActivePanel());
20473         
20474         if (i  < 1 && !this.autoslide) {
20475             return;
20476         }
20477         
20478         if (i < 1 && this.autoslide) {
20479             i = this.tabs.length;
20480         }
20481         
20482         this.showPanel(this.tabs[i-1]);
20483     },
20484     
20485     
20486     addBullet: function()
20487     {
20488         if(!this.bullets || Roo.isTouch){
20489             return;
20490         }
20491         var ctr = this.el.select('.carousel-bullets',true).first();
20492         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20493         var bullet = ctr.createChild({
20494             cls : 'bullet bullet-' + i
20495         },ctr.dom.lastChild);
20496         
20497         
20498         var _this = this;
20499         
20500         bullet.on('click', (function(e, el, o, ii, t){
20501
20502             e.preventDefault();
20503
20504             this.showPanel(ii);
20505
20506             if(this.autoslide && this.slideFn){
20507                 clearInterval(this.slideFn);
20508                 this.slideFn = window.setInterval(function() {
20509                     _this.showPanelNext();
20510                 }, this.timer);
20511             }
20512
20513         }).createDelegate(this, [i, bullet], true));
20514                 
20515         
20516     },
20517      
20518     setActiveBullet : function(i)
20519     {
20520         if(Roo.isTouch){
20521             return;
20522         }
20523         
20524         Roo.each(this.el.select('.bullet', true).elements, function(el){
20525             el.removeClass('selected');
20526         });
20527
20528         var bullet = this.el.select('.bullet-' + i, true).first();
20529         
20530         if(!bullet){
20531             return;
20532         }
20533         
20534         bullet.addClass('selected');
20535     }
20536     
20537     
20538   
20539 });
20540
20541  
20542
20543  
20544  
20545 Roo.apply(Roo.bootstrap.TabGroup, {
20546     
20547     groups: {},
20548      /**
20549     * register a Navigation Group
20550     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20551     */
20552     register : function(navgrp)
20553     {
20554         this.groups[navgrp.navId] = navgrp;
20555         
20556     },
20557     /**
20558     * fetch a Navigation Group based on the navigation ID
20559     * if one does not exist , it will get created.
20560     * @param {string} the navgroup to add
20561     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20562     */
20563     get: function(navId) {
20564         if (typeof(this.groups[navId]) == 'undefined') {
20565             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20566         }
20567         return this.groups[navId] ;
20568     }
20569     
20570     
20571     
20572 });
20573
20574  /*
20575  * - LGPL
20576  *
20577  * TabPanel
20578  * 
20579  */
20580
20581 /**
20582  * @class Roo.bootstrap.TabPanel
20583  * @extends Roo.bootstrap.Component
20584  * Bootstrap TabPanel class
20585  * @cfg {Boolean} active panel active
20586  * @cfg {String} html panel content
20587  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20588  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20589  * @cfg {String} href click to link..
20590  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20591  * 
20592  * 
20593  * @constructor
20594  * Create a new TabPanel
20595  * @param {Object} config The config object
20596  */
20597
20598 Roo.bootstrap.TabPanel = function(config){
20599     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20600     this.addEvents({
20601         /**
20602              * @event changed
20603              * Fires when the active status changes
20604              * @param {Roo.bootstrap.TabPanel} this
20605              * @param {Boolean} state the new state
20606             
20607          */
20608         'changed': true,
20609         /**
20610              * @event beforedeactivate
20611              * Fires before a tab is de-activated - can be used to do validation on a form.
20612              * @param {Roo.bootstrap.TabPanel} this
20613              * @return {Boolean} false if there is an error
20614             
20615          */
20616         'beforedeactivate': true
20617      });
20618     
20619     this.tabId = this.tabId || Roo.id();
20620   
20621 };
20622
20623 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20624     
20625     active: false,
20626     html: false,
20627     tabId: false,
20628     navId : false,
20629     href : '',
20630     touchSlide : false,
20631     getAutoCreate : function(){
20632         
20633         
20634         var cfg = {
20635             tag: 'div',
20636             // item is needed for carousel - not sure if it has any effect otherwise
20637             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20638             html: this.html || ''
20639         };
20640         
20641         if(this.active){
20642             cfg.cls += ' active';
20643         }
20644         
20645         if(this.tabId){
20646             cfg.tabId = this.tabId;
20647         }
20648         
20649         
20650         
20651         return cfg;
20652     },
20653     
20654     initEvents:  function()
20655     {
20656         var p = this.parent();
20657         
20658         this.navId = this.navId || p.navId;
20659         
20660         if (typeof(this.navId) != 'undefined') {
20661             // not really needed.. but just in case.. parent should be a NavGroup.
20662             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20663             
20664             tg.register(this);
20665             
20666             var i = tg.tabs.length - 1;
20667             
20668             if(this.active && tg.bullets > 0 && i < tg.bullets){
20669                 tg.setActiveBullet(i);
20670             }
20671         }
20672         
20673         this.el.on('click', this.onClick, this);
20674         
20675         if(Roo.isTouch && this.touchSlide){
20676             this.el.on("touchstart", this.onTouchStart, this);
20677             this.el.on("touchmove", this.onTouchMove, this);
20678             this.el.on("touchend", this.onTouchEnd, this);
20679         }
20680         
20681     },
20682     
20683     onRender : function(ct, position)
20684     {
20685         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20686     },
20687     
20688     setActive : function(state)
20689     {
20690         Roo.log("panel - set active " + this.tabId + "=" + state);
20691         
20692         this.active = state;
20693         if (!state) {
20694             this.el.removeClass('active');
20695             
20696         } else  if (!this.el.hasClass('active')) {
20697             this.el.addClass('active');
20698         }
20699         
20700         this.fireEvent('changed', this, state);
20701     },
20702     
20703     onClick : function(e)
20704     {
20705         e.preventDefault();
20706         
20707         if(!this.href.length){
20708             return;
20709         }
20710         
20711         window.location.href = this.href;
20712     },
20713     
20714     startX : 0,
20715     startY : 0,
20716     endX : 0,
20717     endY : 0,
20718     swiping : false,
20719     
20720     onTouchStart : function(e)
20721     {
20722         this.swiping = false;
20723         
20724         this.startX = e.browserEvent.touches[0].clientX;
20725         this.startY = e.browserEvent.touches[0].clientY;
20726     },
20727     
20728     onTouchMove : function(e)
20729     {
20730         this.swiping = true;
20731         
20732         this.endX = e.browserEvent.touches[0].clientX;
20733         this.endY = e.browserEvent.touches[0].clientY;
20734     },
20735     
20736     onTouchEnd : function(e)
20737     {
20738         if(!this.swiping){
20739             this.onClick(e);
20740             return;
20741         }
20742         
20743         var tabGroup = this.parent();
20744         
20745         if(this.endX > this.startX){ // swiping right
20746             tabGroup.showPanelPrev();
20747             return;
20748         }
20749         
20750         if(this.startX > this.endX){ // swiping left
20751             tabGroup.showPanelNext();
20752             return;
20753         }
20754     }
20755     
20756     
20757 });
20758  
20759
20760  
20761
20762  /*
20763  * - LGPL
20764  *
20765  * DateField
20766  * 
20767  */
20768
20769 /**
20770  * @class Roo.bootstrap.DateField
20771  * @extends Roo.bootstrap.Input
20772  * Bootstrap DateField class
20773  * @cfg {Number} weekStart default 0
20774  * @cfg {String} viewMode default empty, (months|years)
20775  * @cfg {String} minViewMode default empty, (months|years)
20776  * @cfg {Number} startDate default -Infinity
20777  * @cfg {Number} endDate default Infinity
20778  * @cfg {Boolean} todayHighlight default false
20779  * @cfg {Boolean} todayBtn default false
20780  * @cfg {Boolean} calendarWeeks default false
20781  * @cfg {Object} daysOfWeekDisabled default empty
20782  * @cfg {Boolean} singleMode default false (true | false)
20783  * 
20784  * @cfg {Boolean} keyboardNavigation default true
20785  * @cfg {String} language default en
20786  * 
20787  * @constructor
20788  * Create a new DateField
20789  * @param {Object} config The config object
20790  */
20791
20792 Roo.bootstrap.DateField = function(config){
20793     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20794      this.addEvents({
20795             /**
20796              * @event show
20797              * Fires when this field show.
20798              * @param {Roo.bootstrap.DateField} this
20799              * @param {Mixed} date The date value
20800              */
20801             show : true,
20802             /**
20803              * @event show
20804              * Fires when this field hide.
20805              * @param {Roo.bootstrap.DateField} this
20806              * @param {Mixed} date The date value
20807              */
20808             hide : true,
20809             /**
20810              * @event select
20811              * Fires when select a date.
20812              * @param {Roo.bootstrap.DateField} this
20813              * @param {Mixed} date The date value
20814              */
20815             select : true,
20816             /**
20817              * @event beforeselect
20818              * Fires when before select a date.
20819              * @param {Roo.bootstrap.DateField} this
20820              * @param {Mixed} date The date value
20821              */
20822             beforeselect : true
20823         });
20824 };
20825
20826 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20827     
20828     /**
20829      * @cfg {String} format
20830      * The default date format string which can be overriden for localization support.  The format must be
20831      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20832      */
20833     format : "m/d/y",
20834     /**
20835      * @cfg {String} altFormats
20836      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20837      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20838      */
20839     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20840     
20841     weekStart : 0,
20842     
20843     viewMode : '',
20844     
20845     minViewMode : '',
20846     
20847     todayHighlight : false,
20848     
20849     todayBtn: false,
20850     
20851     language: 'en',
20852     
20853     keyboardNavigation: true,
20854     
20855     calendarWeeks: false,
20856     
20857     startDate: -Infinity,
20858     
20859     endDate: Infinity,
20860     
20861     daysOfWeekDisabled: [],
20862     
20863     _events: [],
20864     
20865     singleMode : false,
20866     
20867     UTCDate: function()
20868     {
20869         return new Date(Date.UTC.apply(Date, arguments));
20870     },
20871     
20872     UTCToday: function()
20873     {
20874         var today = new Date();
20875         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20876     },
20877     
20878     getDate: function() {
20879             var d = this.getUTCDate();
20880             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20881     },
20882     
20883     getUTCDate: function() {
20884             return this.date;
20885     },
20886     
20887     setDate: function(d) {
20888             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20889     },
20890     
20891     setUTCDate: function(d) {
20892             this.date = d;
20893             this.setValue(this.formatDate(this.date));
20894     },
20895         
20896     onRender: function(ct, position)
20897     {
20898         
20899         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20900         
20901         this.language = this.language || 'en';
20902         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20903         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20904         
20905         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20906         this.format = this.format || 'm/d/y';
20907         this.isInline = false;
20908         this.isInput = true;
20909         this.component = this.el.select('.add-on', true).first() || false;
20910         this.component = (this.component && this.component.length === 0) ? false : this.component;
20911         this.hasInput = this.component && this.inputEl().length;
20912         
20913         if (typeof(this.minViewMode === 'string')) {
20914             switch (this.minViewMode) {
20915                 case 'months':
20916                     this.minViewMode = 1;
20917                     break;
20918                 case 'years':
20919                     this.minViewMode = 2;
20920                     break;
20921                 default:
20922                     this.minViewMode = 0;
20923                     break;
20924             }
20925         }
20926         
20927         if (typeof(this.viewMode === 'string')) {
20928             switch (this.viewMode) {
20929                 case 'months':
20930                     this.viewMode = 1;
20931                     break;
20932                 case 'years':
20933                     this.viewMode = 2;
20934                     break;
20935                 default:
20936                     this.viewMode = 0;
20937                     break;
20938             }
20939         }
20940                 
20941         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20942         
20943 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20944         
20945         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20946         
20947         this.picker().on('mousedown', this.onMousedown, this);
20948         this.picker().on('click', this.onClick, this);
20949         
20950         this.picker().addClass('datepicker-dropdown');
20951         
20952         this.startViewMode = this.viewMode;
20953         
20954         if(this.singleMode){
20955             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20956                 v.setVisibilityMode(Roo.Element.DISPLAY);
20957                 v.hide();
20958             });
20959             
20960             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20961                 v.setStyle('width', '189px');
20962             });
20963         }
20964         
20965         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20966             if(!this.calendarWeeks){
20967                 v.remove();
20968                 return;
20969             }
20970             
20971             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20972             v.attr('colspan', function(i, val){
20973                 return parseInt(val) + 1;
20974             });
20975         });
20976                         
20977         
20978         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20979         
20980         this.setStartDate(this.startDate);
20981         this.setEndDate(this.endDate);
20982         
20983         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20984         
20985         this.fillDow();
20986         this.fillMonths();
20987         this.update();
20988         this.showMode();
20989         
20990         if(this.isInline) {
20991             this.showPopup();
20992         }
20993     },
20994     
20995     picker : function()
20996     {
20997         return this.pickerEl;
20998 //        return this.el.select('.datepicker', true).first();
20999     },
21000     
21001     fillDow: function()
21002     {
21003         var dowCnt = this.weekStart;
21004         
21005         var dow = {
21006             tag: 'tr',
21007             cn: [
21008                 
21009             ]
21010         };
21011         
21012         if(this.calendarWeeks){
21013             dow.cn.push({
21014                 tag: 'th',
21015                 cls: 'cw',
21016                 html: '&nbsp;'
21017             })
21018         }
21019         
21020         while (dowCnt < this.weekStart + 7) {
21021             dow.cn.push({
21022                 tag: 'th',
21023                 cls: 'dow',
21024                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21025             });
21026         }
21027         
21028         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21029     },
21030     
21031     fillMonths: function()
21032     {    
21033         var i = 0;
21034         var months = this.picker().select('>.datepicker-months td', true).first();
21035         
21036         months.dom.innerHTML = '';
21037         
21038         while (i < 12) {
21039             var month = {
21040                 tag: 'span',
21041                 cls: 'month',
21042                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21043             };
21044             
21045             months.createChild(month);
21046         }
21047         
21048     },
21049     
21050     update: function()
21051     {
21052         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;
21053         
21054         if (this.date < this.startDate) {
21055             this.viewDate = new Date(this.startDate);
21056         } else if (this.date > this.endDate) {
21057             this.viewDate = new Date(this.endDate);
21058         } else {
21059             this.viewDate = new Date(this.date);
21060         }
21061         
21062         this.fill();
21063     },
21064     
21065     fill: function() 
21066     {
21067         var d = new Date(this.viewDate),
21068                 year = d.getUTCFullYear(),
21069                 month = d.getUTCMonth(),
21070                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21071                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21072                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21073                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21074                 currentDate = this.date && this.date.valueOf(),
21075                 today = this.UTCToday();
21076         
21077         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21078         
21079 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21080         
21081 //        this.picker.select('>tfoot th.today').
21082 //                                              .text(dates[this.language].today)
21083 //                                              .toggle(this.todayBtn !== false);
21084     
21085         this.updateNavArrows();
21086         this.fillMonths();
21087                                                 
21088         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21089         
21090         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21091          
21092         prevMonth.setUTCDate(day);
21093         
21094         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21095         
21096         var nextMonth = new Date(prevMonth);
21097         
21098         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21099         
21100         nextMonth = nextMonth.valueOf();
21101         
21102         var fillMonths = false;
21103         
21104         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21105         
21106         while(prevMonth.valueOf() <= nextMonth) {
21107             var clsName = '';
21108             
21109             if (prevMonth.getUTCDay() === this.weekStart) {
21110                 if(fillMonths){
21111                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21112                 }
21113                     
21114                 fillMonths = {
21115                     tag: 'tr',
21116                     cn: []
21117                 };
21118                 
21119                 if(this.calendarWeeks){
21120                     // ISO 8601: First week contains first thursday.
21121                     // ISO also states week starts on Monday, but we can be more abstract here.
21122                     var
21123                     // Start of current week: based on weekstart/current date
21124                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21125                     // Thursday of this week
21126                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21127                     // First Thursday of year, year from thursday
21128                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21129                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21130                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21131                     
21132                     fillMonths.cn.push({
21133                         tag: 'td',
21134                         cls: 'cw',
21135                         html: calWeek
21136                     });
21137                 }
21138             }
21139             
21140             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21141                 clsName += ' old';
21142             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21143                 clsName += ' new';
21144             }
21145             if (this.todayHighlight &&
21146                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21147                 prevMonth.getUTCMonth() == today.getMonth() &&
21148                 prevMonth.getUTCDate() == today.getDate()) {
21149                 clsName += ' today';
21150             }
21151             
21152             if (currentDate && prevMonth.valueOf() === currentDate) {
21153                 clsName += ' active';
21154             }
21155             
21156             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21157                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21158                     clsName += ' disabled';
21159             }
21160             
21161             fillMonths.cn.push({
21162                 tag: 'td',
21163                 cls: 'day ' + clsName,
21164                 html: prevMonth.getDate()
21165             });
21166             
21167             prevMonth.setDate(prevMonth.getDate()+1);
21168         }
21169           
21170         var currentYear = this.date && this.date.getUTCFullYear();
21171         var currentMonth = this.date && this.date.getUTCMonth();
21172         
21173         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21174         
21175         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21176             v.removeClass('active');
21177             
21178             if(currentYear === year && k === currentMonth){
21179                 v.addClass('active');
21180             }
21181             
21182             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21183                 v.addClass('disabled');
21184             }
21185             
21186         });
21187         
21188         
21189         year = parseInt(year/10, 10) * 10;
21190         
21191         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21192         
21193         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21194         
21195         year -= 1;
21196         for (var i = -1; i < 11; i++) {
21197             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21198                 tag: 'span',
21199                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21200                 html: year
21201             });
21202             
21203             year += 1;
21204         }
21205     },
21206     
21207     showMode: function(dir) 
21208     {
21209         if (dir) {
21210             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21211         }
21212         
21213         Roo.each(this.picker().select('>div',true).elements, function(v){
21214             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21215             v.hide();
21216         });
21217         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21218     },
21219     
21220     place: function()
21221     {
21222         if(this.isInline) {
21223             return;
21224         }
21225         
21226         this.picker().removeClass(['bottom', 'top']);
21227         
21228         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21229             /*
21230              * place to the top of element!
21231              *
21232              */
21233             
21234             this.picker().addClass('top');
21235             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21236             
21237             return;
21238         }
21239         
21240         this.picker().addClass('bottom');
21241         
21242         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21243     },
21244     
21245     parseDate : function(value)
21246     {
21247         if(!value || value instanceof Date){
21248             return value;
21249         }
21250         var v = Date.parseDate(value, this.format);
21251         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21252             v = Date.parseDate(value, 'Y-m-d');
21253         }
21254         if(!v && this.altFormats){
21255             if(!this.altFormatsArray){
21256                 this.altFormatsArray = this.altFormats.split("|");
21257             }
21258             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21259                 v = Date.parseDate(value, this.altFormatsArray[i]);
21260             }
21261         }
21262         return v;
21263     },
21264     
21265     formatDate : function(date, fmt)
21266     {   
21267         return (!date || !(date instanceof Date)) ?
21268         date : date.dateFormat(fmt || this.format);
21269     },
21270     
21271     onFocus : function()
21272     {
21273         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21274         this.showPopup();
21275     },
21276     
21277     onBlur : function()
21278     {
21279         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21280         
21281         var d = this.inputEl().getValue();
21282         
21283         this.setValue(d);
21284                 
21285         this.hidePopup();
21286     },
21287     
21288     showPopup : function()
21289     {
21290         this.picker().show();
21291         this.update();
21292         this.place();
21293         
21294         this.fireEvent('showpopup', this, this.date);
21295     },
21296     
21297     hidePopup : function()
21298     {
21299         if(this.isInline) {
21300             return;
21301         }
21302         this.picker().hide();
21303         this.viewMode = this.startViewMode;
21304         this.showMode();
21305         
21306         this.fireEvent('hidepopup', this, this.date);
21307         
21308     },
21309     
21310     onMousedown: function(e)
21311     {
21312         e.stopPropagation();
21313         e.preventDefault();
21314     },
21315     
21316     keyup: function(e)
21317     {
21318         Roo.bootstrap.DateField.superclass.keyup.call(this);
21319         this.update();
21320     },
21321
21322     setValue: function(v)
21323     {
21324         if(this.fireEvent('beforeselect', this, v) !== false){
21325             var d = new Date(this.parseDate(v) ).clearTime();
21326         
21327             if(isNaN(d.getTime())){
21328                 this.date = this.viewDate = '';
21329                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21330                 return;
21331             }
21332
21333             v = this.formatDate(d);
21334
21335             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21336
21337             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21338
21339             this.update();
21340
21341             this.fireEvent('select', this, this.date);
21342         }
21343     },
21344     
21345     getValue: function()
21346     {
21347         return this.formatDate(this.date);
21348     },
21349     
21350     fireKey: function(e)
21351     {
21352         if (!this.picker().isVisible()){
21353             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21354                 this.showPopup();
21355             }
21356             return;
21357         }
21358         
21359         var dateChanged = false,
21360         dir, day, month,
21361         newDate, newViewDate;
21362         
21363         switch(e.keyCode){
21364             case 27: // escape
21365                 this.hidePopup();
21366                 e.preventDefault();
21367                 break;
21368             case 37: // left
21369             case 39: // right
21370                 if (!this.keyboardNavigation) {
21371                     break;
21372                 }
21373                 dir = e.keyCode == 37 ? -1 : 1;
21374                 
21375                 if (e.ctrlKey){
21376                     newDate = this.moveYear(this.date, dir);
21377                     newViewDate = this.moveYear(this.viewDate, dir);
21378                 } else if (e.shiftKey){
21379                     newDate = this.moveMonth(this.date, dir);
21380                     newViewDate = this.moveMonth(this.viewDate, dir);
21381                 } else {
21382                     newDate = new Date(this.date);
21383                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21384                     newViewDate = new Date(this.viewDate);
21385                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21386                 }
21387                 if (this.dateWithinRange(newDate)){
21388                     this.date = newDate;
21389                     this.viewDate = newViewDate;
21390                     this.setValue(this.formatDate(this.date));
21391 //                    this.update();
21392                     e.preventDefault();
21393                     dateChanged = true;
21394                 }
21395                 break;
21396             case 38: // up
21397             case 40: // down
21398                 if (!this.keyboardNavigation) {
21399                     break;
21400                 }
21401                 dir = e.keyCode == 38 ? -1 : 1;
21402                 if (e.ctrlKey){
21403                     newDate = this.moveYear(this.date, dir);
21404                     newViewDate = this.moveYear(this.viewDate, dir);
21405                 } else if (e.shiftKey){
21406                     newDate = this.moveMonth(this.date, dir);
21407                     newViewDate = this.moveMonth(this.viewDate, dir);
21408                 } else {
21409                     newDate = new Date(this.date);
21410                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21411                     newViewDate = new Date(this.viewDate);
21412                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21413                 }
21414                 if (this.dateWithinRange(newDate)){
21415                     this.date = newDate;
21416                     this.viewDate = newViewDate;
21417                     this.setValue(this.formatDate(this.date));
21418 //                    this.update();
21419                     e.preventDefault();
21420                     dateChanged = true;
21421                 }
21422                 break;
21423             case 13: // enter
21424                 this.setValue(this.formatDate(this.date));
21425                 this.hidePopup();
21426                 e.preventDefault();
21427                 break;
21428             case 9: // tab
21429                 this.setValue(this.formatDate(this.date));
21430                 this.hidePopup();
21431                 break;
21432             case 16: // shift
21433             case 17: // ctrl
21434             case 18: // alt
21435                 break;
21436             default :
21437                 this.hidePopup();
21438                 
21439         }
21440     },
21441     
21442     
21443     onClick: function(e) 
21444     {
21445         e.stopPropagation();
21446         e.preventDefault();
21447         
21448         var target = e.getTarget();
21449         
21450         if(target.nodeName.toLowerCase() === 'i'){
21451             target = Roo.get(target).dom.parentNode;
21452         }
21453         
21454         var nodeName = target.nodeName;
21455         var className = target.className;
21456         var html = target.innerHTML;
21457         //Roo.log(nodeName);
21458         
21459         switch(nodeName.toLowerCase()) {
21460             case 'th':
21461                 switch(className) {
21462                     case 'switch':
21463                         this.showMode(1);
21464                         break;
21465                     case 'prev':
21466                     case 'next':
21467                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21468                         switch(this.viewMode){
21469                                 case 0:
21470                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21471                                         break;
21472                                 case 1:
21473                                 case 2:
21474                                         this.viewDate = this.moveYear(this.viewDate, dir);
21475                                         break;
21476                         }
21477                         this.fill();
21478                         break;
21479                     case 'today':
21480                         var date = new Date();
21481                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21482 //                        this.fill()
21483                         this.setValue(this.formatDate(this.date));
21484                         
21485                         this.hidePopup();
21486                         break;
21487                 }
21488                 break;
21489             case 'span':
21490                 if (className.indexOf('disabled') < 0) {
21491                     this.viewDate.setUTCDate(1);
21492                     if (className.indexOf('month') > -1) {
21493                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21494                     } else {
21495                         var year = parseInt(html, 10) || 0;
21496                         this.viewDate.setUTCFullYear(year);
21497                         
21498                     }
21499                     
21500                     if(this.singleMode){
21501                         this.setValue(this.formatDate(this.viewDate));
21502                         this.hidePopup();
21503                         return;
21504                     }
21505                     
21506                     this.showMode(-1);
21507                     this.fill();
21508                 }
21509                 break;
21510                 
21511             case 'td':
21512                 //Roo.log(className);
21513                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21514                     var day = parseInt(html, 10) || 1;
21515                     var year = this.viewDate.getUTCFullYear(),
21516                         month = this.viewDate.getUTCMonth();
21517
21518                     if (className.indexOf('old') > -1) {
21519                         if(month === 0 ){
21520                             month = 11;
21521                             year -= 1;
21522                         }else{
21523                             month -= 1;
21524                         }
21525                     } else if (className.indexOf('new') > -1) {
21526                         if (month == 11) {
21527                             month = 0;
21528                             year += 1;
21529                         } else {
21530                             month += 1;
21531                         }
21532                     }
21533                     //Roo.log([year,month,day]);
21534                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21535                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21536 //                    this.fill();
21537                     //Roo.log(this.formatDate(this.date));
21538                     this.setValue(this.formatDate(this.date));
21539                     this.hidePopup();
21540                 }
21541                 break;
21542         }
21543     },
21544     
21545     setStartDate: function(startDate)
21546     {
21547         this.startDate = startDate || -Infinity;
21548         if (this.startDate !== -Infinity) {
21549             this.startDate = this.parseDate(this.startDate);
21550         }
21551         this.update();
21552         this.updateNavArrows();
21553     },
21554
21555     setEndDate: function(endDate)
21556     {
21557         this.endDate = endDate || Infinity;
21558         if (this.endDate !== Infinity) {
21559             this.endDate = this.parseDate(this.endDate);
21560         }
21561         this.update();
21562         this.updateNavArrows();
21563     },
21564     
21565     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21566     {
21567         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21568         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21569             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21570         }
21571         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21572             return parseInt(d, 10);
21573         });
21574         this.update();
21575         this.updateNavArrows();
21576     },
21577     
21578     updateNavArrows: function() 
21579     {
21580         if(this.singleMode){
21581             return;
21582         }
21583         
21584         var d = new Date(this.viewDate),
21585         year = d.getUTCFullYear(),
21586         month = d.getUTCMonth();
21587         
21588         Roo.each(this.picker().select('.prev', true).elements, function(v){
21589             v.show();
21590             switch (this.viewMode) {
21591                 case 0:
21592
21593                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21594                         v.hide();
21595                     }
21596                     break;
21597                 case 1:
21598                 case 2:
21599                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21600                         v.hide();
21601                     }
21602                     break;
21603             }
21604         });
21605         
21606         Roo.each(this.picker().select('.next', true).elements, function(v){
21607             v.show();
21608             switch (this.viewMode) {
21609                 case 0:
21610
21611                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21612                         v.hide();
21613                     }
21614                     break;
21615                 case 1:
21616                 case 2:
21617                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21618                         v.hide();
21619                     }
21620                     break;
21621             }
21622         })
21623     },
21624     
21625     moveMonth: function(date, dir)
21626     {
21627         if (!dir) {
21628             return date;
21629         }
21630         var new_date = new Date(date.valueOf()),
21631         day = new_date.getUTCDate(),
21632         month = new_date.getUTCMonth(),
21633         mag = Math.abs(dir),
21634         new_month, test;
21635         dir = dir > 0 ? 1 : -1;
21636         if (mag == 1){
21637             test = dir == -1
21638             // If going back one month, make sure month is not current month
21639             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21640             ? function(){
21641                 return new_date.getUTCMonth() == month;
21642             }
21643             // If going forward one month, make sure month is as expected
21644             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21645             : function(){
21646                 return new_date.getUTCMonth() != new_month;
21647             };
21648             new_month = month + dir;
21649             new_date.setUTCMonth(new_month);
21650             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21651             if (new_month < 0 || new_month > 11) {
21652                 new_month = (new_month + 12) % 12;
21653             }
21654         } else {
21655             // For magnitudes >1, move one month at a time...
21656             for (var i=0; i<mag; i++) {
21657                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21658                 new_date = this.moveMonth(new_date, dir);
21659             }
21660             // ...then reset the day, keeping it in the new month
21661             new_month = new_date.getUTCMonth();
21662             new_date.setUTCDate(day);
21663             test = function(){
21664                 return new_month != new_date.getUTCMonth();
21665             };
21666         }
21667         // Common date-resetting loop -- if date is beyond end of month, make it
21668         // end of month
21669         while (test()){
21670             new_date.setUTCDate(--day);
21671             new_date.setUTCMonth(new_month);
21672         }
21673         return new_date;
21674     },
21675
21676     moveYear: function(date, dir)
21677     {
21678         return this.moveMonth(date, dir*12);
21679     },
21680
21681     dateWithinRange: function(date)
21682     {
21683         return date >= this.startDate && date <= this.endDate;
21684     },
21685
21686     
21687     remove: function() 
21688     {
21689         this.picker().remove();
21690     },
21691     
21692     validateValue : function(value)
21693     {
21694         if(this.getVisibilityEl().hasClass('hidden')){
21695             return true;
21696         }
21697         
21698         if(value.length < 1)  {
21699             if(this.allowBlank){
21700                 return true;
21701             }
21702             return false;
21703         }
21704         
21705         if(value.length < this.minLength){
21706             return false;
21707         }
21708         if(value.length > this.maxLength){
21709             return false;
21710         }
21711         if(this.vtype){
21712             var vt = Roo.form.VTypes;
21713             if(!vt[this.vtype](value, this)){
21714                 return false;
21715             }
21716         }
21717         if(typeof this.validator == "function"){
21718             var msg = this.validator(value);
21719             if(msg !== true){
21720                 return false;
21721             }
21722         }
21723         
21724         if(this.regex && !this.regex.test(value)){
21725             return false;
21726         }
21727         
21728         if(typeof(this.parseDate(value)) == 'undefined'){
21729             return false;
21730         }
21731         
21732         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21733             return false;
21734         }      
21735         
21736         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21737             return false;
21738         } 
21739         
21740         
21741         return true;
21742     },
21743     
21744     reset : function()
21745     {
21746         this.date = this.viewDate = '';
21747         
21748         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21749     }
21750    
21751 });
21752
21753 Roo.apply(Roo.bootstrap.DateField,  {
21754     
21755     head : {
21756         tag: 'thead',
21757         cn: [
21758         {
21759             tag: 'tr',
21760             cn: [
21761             {
21762                 tag: 'th',
21763                 cls: 'prev',
21764                 html: '<i class="fa fa-arrow-left"/>'
21765             },
21766             {
21767                 tag: 'th',
21768                 cls: 'switch',
21769                 colspan: '5'
21770             },
21771             {
21772                 tag: 'th',
21773                 cls: 'next',
21774                 html: '<i class="fa fa-arrow-right"/>'
21775             }
21776
21777             ]
21778         }
21779         ]
21780     },
21781     
21782     content : {
21783         tag: 'tbody',
21784         cn: [
21785         {
21786             tag: 'tr',
21787             cn: [
21788             {
21789                 tag: 'td',
21790                 colspan: '7'
21791             }
21792             ]
21793         }
21794         ]
21795     },
21796     
21797     footer : {
21798         tag: 'tfoot',
21799         cn: [
21800         {
21801             tag: 'tr',
21802             cn: [
21803             {
21804                 tag: 'th',
21805                 colspan: '7',
21806                 cls: 'today'
21807             }
21808                     
21809             ]
21810         }
21811         ]
21812     },
21813     
21814     dates:{
21815         en: {
21816             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21817             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21818             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21819             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21820             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21821             today: "Today"
21822         }
21823     },
21824     
21825     modes: [
21826     {
21827         clsName: 'days',
21828         navFnc: 'Month',
21829         navStep: 1
21830     },
21831     {
21832         clsName: 'months',
21833         navFnc: 'FullYear',
21834         navStep: 1
21835     },
21836     {
21837         clsName: 'years',
21838         navFnc: 'FullYear',
21839         navStep: 10
21840     }]
21841 });
21842
21843 Roo.apply(Roo.bootstrap.DateField,  {
21844   
21845     template : {
21846         tag: 'div',
21847         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21848         cn: [
21849         {
21850             tag: 'div',
21851             cls: 'datepicker-days',
21852             cn: [
21853             {
21854                 tag: 'table',
21855                 cls: 'table-condensed',
21856                 cn:[
21857                 Roo.bootstrap.DateField.head,
21858                 {
21859                     tag: 'tbody'
21860                 },
21861                 Roo.bootstrap.DateField.footer
21862                 ]
21863             }
21864             ]
21865         },
21866         {
21867             tag: 'div',
21868             cls: 'datepicker-months',
21869             cn: [
21870             {
21871                 tag: 'table',
21872                 cls: 'table-condensed',
21873                 cn:[
21874                 Roo.bootstrap.DateField.head,
21875                 Roo.bootstrap.DateField.content,
21876                 Roo.bootstrap.DateField.footer
21877                 ]
21878             }
21879             ]
21880         },
21881         {
21882             tag: 'div',
21883             cls: 'datepicker-years',
21884             cn: [
21885             {
21886                 tag: 'table',
21887                 cls: 'table-condensed',
21888                 cn:[
21889                 Roo.bootstrap.DateField.head,
21890                 Roo.bootstrap.DateField.content,
21891                 Roo.bootstrap.DateField.footer
21892                 ]
21893             }
21894             ]
21895         }
21896         ]
21897     }
21898 });
21899
21900  
21901
21902  /*
21903  * - LGPL
21904  *
21905  * TimeField
21906  * 
21907  */
21908
21909 /**
21910  * @class Roo.bootstrap.TimeField
21911  * @extends Roo.bootstrap.Input
21912  * Bootstrap DateField class
21913  * 
21914  * 
21915  * @constructor
21916  * Create a new TimeField
21917  * @param {Object} config The config object
21918  */
21919
21920 Roo.bootstrap.TimeField = function(config){
21921     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21922     this.addEvents({
21923             /**
21924              * @event show
21925              * Fires when this field show.
21926              * @param {Roo.bootstrap.DateField} thisthis
21927              * @param {Mixed} date The date value
21928              */
21929             show : true,
21930             /**
21931              * @event show
21932              * Fires when this field hide.
21933              * @param {Roo.bootstrap.DateField} this
21934              * @param {Mixed} date The date value
21935              */
21936             hide : true,
21937             /**
21938              * @event select
21939              * Fires when select a date.
21940              * @param {Roo.bootstrap.DateField} this
21941              * @param {Mixed} date The date value
21942              */
21943             select : true
21944         });
21945 };
21946
21947 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21948     
21949     /**
21950      * @cfg {String} format
21951      * The default time format string which can be overriden for localization support.  The format must be
21952      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21953      */
21954     format : "H:i",
21955        
21956     onRender: function(ct, position)
21957     {
21958         
21959         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21960                 
21961         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21962         
21963         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21964         
21965         this.pop = this.picker().select('>.datepicker-time',true).first();
21966         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21967         
21968         this.picker().on('mousedown', this.onMousedown, this);
21969         this.picker().on('click', this.onClick, this);
21970         
21971         this.picker().addClass('datepicker-dropdown');
21972     
21973         this.fillTime();
21974         this.update();
21975             
21976         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21977         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21978         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21979         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21980         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21981         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21982
21983     },
21984     
21985     fireKey: function(e){
21986         if (!this.picker().isVisible()){
21987             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21988                 this.show();
21989             }
21990             return;
21991         }
21992
21993         e.preventDefault();
21994         
21995         switch(e.keyCode){
21996             case 27: // escape
21997                 this.hide();
21998                 break;
21999             case 37: // left
22000             case 39: // right
22001                 this.onTogglePeriod();
22002                 break;
22003             case 38: // up
22004                 this.onIncrementMinutes();
22005                 break;
22006             case 40: // down
22007                 this.onDecrementMinutes();
22008                 break;
22009             case 13: // enter
22010             case 9: // tab
22011                 this.setTime();
22012                 break;
22013         }
22014     },
22015     
22016     onClick: function(e) {
22017         e.stopPropagation();
22018         e.preventDefault();
22019     },
22020     
22021     picker : function()
22022     {
22023         return this.el.select('.datepicker', true).first();
22024     },
22025     
22026     fillTime: function()
22027     {    
22028         var time = this.pop.select('tbody', true).first();
22029         
22030         time.dom.innerHTML = '';
22031         
22032         time.createChild({
22033             tag: 'tr',
22034             cn: [
22035                 {
22036                     tag: 'td',
22037                     cn: [
22038                         {
22039                             tag: 'a',
22040                             href: '#',
22041                             cls: 'btn',
22042                             cn: [
22043                                 {
22044                                     tag: 'span',
22045                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
22046                                 }
22047                             ]
22048                         } 
22049                     ]
22050                 },
22051                 {
22052                     tag: 'td',
22053                     cls: 'separator'
22054                 },
22055                 {
22056                     tag: 'td',
22057                     cn: [
22058                         {
22059                             tag: 'a',
22060                             href: '#',
22061                             cls: 'btn',
22062                             cn: [
22063                                 {
22064                                     tag: 'span',
22065                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
22066                                 }
22067                             ]
22068                         }
22069                     ]
22070                 },
22071                 {
22072                     tag: 'td',
22073                     cls: 'separator'
22074                 }
22075             ]
22076         });
22077         
22078         time.createChild({
22079             tag: 'tr',
22080             cn: [
22081                 {
22082                     tag: 'td',
22083                     cn: [
22084                         {
22085                             tag: 'span',
22086                             cls: 'timepicker-hour',
22087                             html: '00'
22088                         }  
22089                     ]
22090                 },
22091                 {
22092                     tag: 'td',
22093                     cls: 'separator',
22094                     html: ':'
22095                 },
22096                 {
22097                     tag: 'td',
22098                     cn: [
22099                         {
22100                             tag: 'span',
22101                             cls: 'timepicker-minute',
22102                             html: '00'
22103                         }  
22104                     ]
22105                 },
22106                 {
22107                     tag: 'td',
22108                     cls: 'separator'
22109                 },
22110                 {
22111                     tag: 'td',
22112                     cn: [
22113                         {
22114                             tag: 'button',
22115                             type: 'button',
22116                             cls: 'btn btn-primary period',
22117                             html: 'AM'
22118                             
22119                         }
22120                     ]
22121                 }
22122             ]
22123         });
22124         
22125         time.createChild({
22126             tag: 'tr',
22127             cn: [
22128                 {
22129                     tag: 'td',
22130                     cn: [
22131                         {
22132                             tag: 'a',
22133                             href: '#',
22134                             cls: 'btn',
22135                             cn: [
22136                                 {
22137                                     tag: 'span',
22138                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
22139                                 }
22140                             ]
22141                         }
22142                     ]
22143                 },
22144                 {
22145                     tag: 'td',
22146                     cls: 'separator'
22147                 },
22148                 {
22149                     tag: 'td',
22150                     cn: [
22151                         {
22152                             tag: 'a',
22153                             href: '#',
22154                             cls: 'btn',
22155                             cn: [
22156                                 {
22157                                     tag: 'span',
22158                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
22159                                 }
22160                             ]
22161                         }
22162                     ]
22163                 },
22164                 {
22165                     tag: 'td',
22166                     cls: 'separator'
22167                 }
22168             ]
22169         });
22170         
22171     },
22172     
22173     update: function()
22174     {
22175         
22176         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22177         
22178         this.fill();
22179     },
22180     
22181     fill: function() 
22182     {
22183         var hours = this.time.getHours();
22184         var minutes = this.time.getMinutes();
22185         var period = 'AM';
22186         
22187         if(hours > 11){
22188             period = 'PM';
22189         }
22190         
22191         if(hours == 0){
22192             hours = 12;
22193         }
22194         
22195         
22196         if(hours > 12){
22197             hours = hours - 12;
22198         }
22199         
22200         if(hours < 10){
22201             hours = '0' + hours;
22202         }
22203         
22204         if(minutes < 10){
22205             minutes = '0' + minutes;
22206         }
22207         
22208         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22209         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22210         this.pop.select('button', true).first().dom.innerHTML = period;
22211         
22212     },
22213     
22214     place: function()
22215     {   
22216         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22217         
22218         var cls = ['bottom'];
22219         
22220         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22221             cls.pop();
22222             cls.push('top');
22223         }
22224         
22225         cls.push('right');
22226         
22227         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22228             cls.pop();
22229             cls.push('left');
22230         }
22231         
22232         this.picker().addClass(cls.join('-'));
22233         
22234         var _this = this;
22235         
22236         Roo.each(cls, function(c){
22237             if(c == 'bottom'){
22238                 _this.picker().setTop(_this.inputEl().getHeight());
22239                 return;
22240             }
22241             if(c == 'top'){
22242                 _this.picker().setTop(0 - _this.picker().getHeight());
22243                 return;
22244             }
22245             
22246             if(c == 'left'){
22247                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22248                 return;
22249             }
22250             if(c == 'right'){
22251                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22252                 return;
22253             }
22254         });
22255         
22256     },
22257   
22258     onFocus : function()
22259     {
22260         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22261         this.show();
22262     },
22263     
22264     onBlur : function()
22265     {
22266         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22267         this.hide();
22268     },
22269     
22270     show : function()
22271     {
22272         this.picker().show();
22273         this.pop.show();
22274         this.update();
22275         this.place();
22276         
22277         this.fireEvent('show', this, this.date);
22278     },
22279     
22280     hide : function()
22281     {
22282         this.picker().hide();
22283         this.pop.hide();
22284         
22285         this.fireEvent('hide', this, this.date);
22286     },
22287     
22288     setTime : function()
22289     {
22290         this.hide();
22291         this.setValue(this.time.format(this.format));
22292         
22293         this.fireEvent('select', this, this.date);
22294         
22295         
22296     },
22297     
22298     onMousedown: function(e){
22299         e.stopPropagation();
22300         e.preventDefault();
22301     },
22302     
22303     onIncrementHours: function()
22304     {
22305         Roo.log('onIncrementHours');
22306         this.time = this.time.add(Date.HOUR, 1);
22307         this.update();
22308         
22309     },
22310     
22311     onDecrementHours: function()
22312     {
22313         Roo.log('onDecrementHours');
22314         this.time = this.time.add(Date.HOUR, -1);
22315         this.update();
22316     },
22317     
22318     onIncrementMinutes: function()
22319     {
22320         Roo.log('onIncrementMinutes');
22321         this.time = this.time.add(Date.MINUTE, 1);
22322         this.update();
22323     },
22324     
22325     onDecrementMinutes: function()
22326     {
22327         Roo.log('onDecrementMinutes');
22328         this.time = this.time.add(Date.MINUTE, -1);
22329         this.update();
22330     },
22331     
22332     onTogglePeriod: function()
22333     {
22334         Roo.log('onTogglePeriod');
22335         this.time = this.time.add(Date.HOUR, 12);
22336         this.update();
22337     }
22338     
22339    
22340 });
22341
22342 Roo.apply(Roo.bootstrap.TimeField,  {
22343     
22344     content : {
22345         tag: 'tbody',
22346         cn: [
22347             {
22348                 tag: 'tr',
22349                 cn: [
22350                 {
22351                     tag: 'td',
22352                     colspan: '7'
22353                 }
22354                 ]
22355             }
22356         ]
22357     },
22358     
22359     footer : {
22360         tag: 'tfoot',
22361         cn: [
22362             {
22363                 tag: 'tr',
22364                 cn: [
22365                 {
22366                     tag: 'th',
22367                     colspan: '7',
22368                     cls: '',
22369                     cn: [
22370                         {
22371                             tag: 'button',
22372                             cls: 'btn btn-info ok',
22373                             html: 'OK'
22374                         }
22375                     ]
22376                 }
22377
22378                 ]
22379             }
22380         ]
22381     }
22382 });
22383
22384 Roo.apply(Roo.bootstrap.TimeField,  {
22385   
22386     template : {
22387         tag: 'div',
22388         cls: 'datepicker dropdown-menu',
22389         cn: [
22390             {
22391                 tag: 'div',
22392                 cls: 'datepicker-time',
22393                 cn: [
22394                 {
22395                     tag: 'table',
22396                     cls: 'table-condensed',
22397                     cn:[
22398                     Roo.bootstrap.TimeField.content,
22399                     Roo.bootstrap.TimeField.footer
22400                     ]
22401                 }
22402                 ]
22403             }
22404         ]
22405     }
22406 });
22407
22408  
22409
22410  /*
22411  * - LGPL
22412  *
22413  * MonthField
22414  * 
22415  */
22416
22417 /**
22418  * @class Roo.bootstrap.MonthField
22419  * @extends Roo.bootstrap.Input
22420  * Bootstrap MonthField class
22421  * 
22422  * @cfg {String} language default en
22423  * 
22424  * @constructor
22425  * Create a new MonthField
22426  * @param {Object} config The config object
22427  */
22428
22429 Roo.bootstrap.MonthField = function(config){
22430     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22431     
22432     this.addEvents({
22433         /**
22434          * @event show
22435          * Fires when this field show.
22436          * @param {Roo.bootstrap.MonthField} this
22437          * @param {Mixed} date The date value
22438          */
22439         show : true,
22440         /**
22441          * @event show
22442          * Fires when this field hide.
22443          * @param {Roo.bootstrap.MonthField} this
22444          * @param {Mixed} date The date value
22445          */
22446         hide : true,
22447         /**
22448          * @event select
22449          * Fires when select a date.
22450          * @param {Roo.bootstrap.MonthField} this
22451          * @param {String} oldvalue The old value
22452          * @param {String} newvalue The new value
22453          */
22454         select : true
22455     });
22456 };
22457
22458 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22459     
22460     onRender: function(ct, position)
22461     {
22462         
22463         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22464         
22465         this.language = this.language || 'en';
22466         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22467         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22468         
22469         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22470         this.isInline = false;
22471         this.isInput = true;
22472         this.component = this.el.select('.add-on', true).first() || false;
22473         this.component = (this.component && this.component.length === 0) ? false : this.component;
22474         this.hasInput = this.component && this.inputEL().length;
22475         
22476         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22477         
22478         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22479         
22480         this.picker().on('mousedown', this.onMousedown, this);
22481         this.picker().on('click', this.onClick, this);
22482         
22483         this.picker().addClass('datepicker-dropdown');
22484         
22485         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22486             v.setStyle('width', '189px');
22487         });
22488         
22489         this.fillMonths();
22490         
22491         this.update();
22492         
22493         if(this.isInline) {
22494             this.show();
22495         }
22496         
22497     },
22498     
22499     setValue: function(v, suppressEvent)
22500     {   
22501         var o = this.getValue();
22502         
22503         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22504         
22505         this.update();
22506
22507         if(suppressEvent !== true){
22508             this.fireEvent('select', this, o, v);
22509         }
22510         
22511     },
22512     
22513     getValue: function()
22514     {
22515         return this.value;
22516     },
22517     
22518     onClick: function(e) 
22519     {
22520         e.stopPropagation();
22521         e.preventDefault();
22522         
22523         var target = e.getTarget();
22524         
22525         if(target.nodeName.toLowerCase() === 'i'){
22526             target = Roo.get(target).dom.parentNode;
22527         }
22528         
22529         var nodeName = target.nodeName;
22530         var className = target.className;
22531         var html = target.innerHTML;
22532         
22533         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22534             return;
22535         }
22536         
22537         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22538         
22539         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22540         
22541         this.hide();
22542                         
22543     },
22544     
22545     picker : function()
22546     {
22547         return this.pickerEl;
22548     },
22549     
22550     fillMonths: function()
22551     {    
22552         var i = 0;
22553         var months = this.picker().select('>.datepicker-months td', true).first();
22554         
22555         months.dom.innerHTML = '';
22556         
22557         while (i < 12) {
22558             var month = {
22559                 tag: 'span',
22560                 cls: 'month',
22561                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22562             };
22563             
22564             months.createChild(month);
22565         }
22566         
22567     },
22568     
22569     update: function()
22570     {
22571         var _this = this;
22572         
22573         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22574             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22575         }
22576         
22577         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22578             e.removeClass('active');
22579             
22580             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22581                 e.addClass('active');
22582             }
22583         })
22584     },
22585     
22586     place: function()
22587     {
22588         if(this.isInline) {
22589             return;
22590         }
22591         
22592         this.picker().removeClass(['bottom', 'top']);
22593         
22594         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22595             /*
22596              * place to the top of element!
22597              *
22598              */
22599             
22600             this.picker().addClass('top');
22601             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22602             
22603             return;
22604         }
22605         
22606         this.picker().addClass('bottom');
22607         
22608         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22609     },
22610     
22611     onFocus : function()
22612     {
22613         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22614         this.show();
22615     },
22616     
22617     onBlur : function()
22618     {
22619         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22620         
22621         var d = this.inputEl().getValue();
22622         
22623         this.setValue(d);
22624                 
22625         this.hide();
22626     },
22627     
22628     show : function()
22629     {
22630         this.picker().show();
22631         this.picker().select('>.datepicker-months', true).first().show();
22632         this.update();
22633         this.place();
22634         
22635         this.fireEvent('show', this, this.date);
22636     },
22637     
22638     hide : function()
22639     {
22640         if(this.isInline) {
22641             return;
22642         }
22643         this.picker().hide();
22644         this.fireEvent('hide', this, this.date);
22645         
22646     },
22647     
22648     onMousedown: function(e)
22649     {
22650         e.stopPropagation();
22651         e.preventDefault();
22652     },
22653     
22654     keyup: function(e)
22655     {
22656         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22657         this.update();
22658     },
22659
22660     fireKey: function(e)
22661     {
22662         if (!this.picker().isVisible()){
22663             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22664                 this.show();
22665             }
22666             return;
22667         }
22668         
22669         var dir;
22670         
22671         switch(e.keyCode){
22672             case 27: // escape
22673                 this.hide();
22674                 e.preventDefault();
22675                 break;
22676             case 37: // left
22677             case 39: // right
22678                 dir = e.keyCode == 37 ? -1 : 1;
22679                 
22680                 this.vIndex = this.vIndex + dir;
22681                 
22682                 if(this.vIndex < 0){
22683                     this.vIndex = 0;
22684                 }
22685                 
22686                 if(this.vIndex > 11){
22687                     this.vIndex = 11;
22688                 }
22689                 
22690                 if(isNaN(this.vIndex)){
22691                     this.vIndex = 0;
22692                 }
22693                 
22694                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22695                 
22696                 break;
22697             case 38: // up
22698             case 40: // down
22699                 
22700                 dir = e.keyCode == 38 ? -1 : 1;
22701                 
22702                 this.vIndex = this.vIndex + dir * 4;
22703                 
22704                 if(this.vIndex < 0){
22705                     this.vIndex = 0;
22706                 }
22707                 
22708                 if(this.vIndex > 11){
22709                     this.vIndex = 11;
22710                 }
22711                 
22712                 if(isNaN(this.vIndex)){
22713                     this.vIndex = 0;
22714                 }
22715                 
22716                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22717                 break;
22718                 
22719             case 13: // enter
22720                 
22721                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22722                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22723                 }
22724                 
22725                 this.hide();
22726                 e.preventDefault();
22727                 break;
22728             case 9: // tab
22729                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22730                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22731                 }
22732                 this.hide();
22733                 break;
22734             case 16: // shift
22735             case 17: // ctrl
22736             case 18: // alt
22737                 break;
22738             default :
22739                 this.hide();
22740                 
22741         }
22742     },
22743     
22744     remove: function() 
22745     {
22746         this.picker().remove();
22747     }
22748    
22749 });
22750
22751 Roo.apply(Roo.bootstrap.MonthField,  {
22752     
22753     content : {
22754         tag: 'tbody',
22755         cn: [
22756         {
22757             tag: 'tr',
22758             cn: [
22759             {
22760                 tag: 'td',
22761                 colspan: '7'
22762             }
22763             ]
22764         }
22765         ]
22766     },
22767     
22768     dates:{
22769         en: {
22770             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22771             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22772         }
22773     }
22774 });
22775
22776 Roo.apply(Roo.bootstrap.MonthField,  {
22777   
22778     template : {
22779         tag: 'div',
22780         cls: 'datepicker dropdown-menu roo-dynamic',
22781         cn: [
22782             {
22783                 tag: 'div',
22784                 cls: 'datepicker-months',
22785                 cn: [
22786                 {
22787                     tag: 'table',
22788                     cls: 'table-condensed',
22789                     cn:[
22790                         Roo.bootstrap.DateField.content
22791                     ]
22792                 }
22793                 ]
22794             }
22795         ]
22796     }
22797 });
22798
22799  
22800
22801  
22802  /*
22803  * - LGPL
22804  *
22805  * CheckBox
22806  * 
22807  */
22808
22809 /**
22810  * @class Roo.bootstrap.CheckBox
22811  * @extends Roo.bootstrap.Input
22812  * Bootstrap CheckBox class
22813  * 
22814  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22815  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22816  * @cfg {String} boxLabel The text that appears beside the checkbox
22817  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22818  * @cfg {Boolean} checked initnal the element
22819  * @cfg {Boolean} inline inline the element (default false)
22820  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22821  * @cfg {String} tooltip label tooltip
22822  * 
22823  * @constructor
22824  * Create a new CheckBox
22825  * @param {Object} config The config object
22826  */
22827
22828 Roo.bootstrap.CheckBox = function(config){
22829     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22830    
22831     this.addEvents({
22832         /**
22833         * @event check
22834         * Fires when the element is checked or unchecked.
22835         * @param {Roo.bootstrap.CheckBox} this This input
22836         * @param {Boolean} checked The new checked value
22837         */
22838        check : true,
22839        /**
22840         * @event click
22841         * Fires when the element is click.
22842         * @param {Roo.bootstrap.CheckBox} this This input
22843         */
22844        click : true
22845     });
22846     
22847 };
22848
22849 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22850   
22851     inputType: 'checkbox',
22852     inputValue: 1,
22853     valueOff: 0,
22854     boxLabel: false,
22855     checked: false,
22856     weight : false,
22857     inline: false,
22858     tooltip : '',
22859     
22860     // checkbox success does not make any sense really.. 
22861     invalidClass : "",
22862     validClass : "",
22863     
22864     
22865     getAutoCreate : function()
22866     {
22867         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22868         
22869         var id = Roo.id();
22870         
22871         var cfg = {};
22872         
22873         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22874         
22875         if(this.inline){
22876             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22877         }
22878         
22879         var input =  {
22880             tag: 'input',
22881             id : id,
22882             type : this.inputType,
22883             value : this.inputValue,
22884             cls : 'roo-' + this.inputType, //'form-box',
22885             placeholder : this.placeholder || ''
22886             
22887         };
22888         
22889         if(this.inputType != 'radio'){
22890             var hidden =  {
22891                 tag: 'input',
22892                 type : 'hidden',
22893                 cls : 'roo-hidden-value',
22894                 value : this.checked ? this.inputValue : this.valueOff
22895             };
22896         }
22897         
22898             
22899         if (this.weight) { // Validity check?
22900             cfg.cls += " " + this.inputType + "-" + this.weight;
22901         }
22902         
22903         if (this.disabled) {
22904             input.disabled=true;
22905         }
22906         
22907         if(this.checked){
22908             input.checked = this.checked;
22909         }
22910         
22911         if (this.name) {
22912             
22913             input.name = this.name;
22914             
22915             if(this.inputType != 'radio'){
22916                 hidden.name = this.name;
22917                 input.name = '_hidden_' + this.name;
22918             }
22919         }
22920         
22921         if (this.size) {
22922             input.cls += ' input-' + this.size;
22923         }
22924         
22925         var settings=this;
22926         
22927         ['xs','sm','md','lg'].map(function(size){
22928             if (settings[size]) {
22929                 cfg.cls += ' col-' + size + '-' + settings[size];
22930             }
22931         });
22932         
22933         var inputblock = input;
22934          
22935         if (this.before || this.after) {
22936             
22937             inputblock = {
22938                 cls : 'input-group',
22939                 cn :  [] 
22940             };
22941             
22942             if (this.before) {
22943                 inputblock.cn.push({
22944                     tag :'span',
22945                     cls : 'input-group-addon',
22946                     html : this.before
22947                 });
22948             }
22949             
22950             inputblock.cn.push(input);
22951             
22952             if(this.inputType != 'radio'){
22953                 inputblock.cn.push(hidden);
22954             }
22955             
22956             if (this.after) {
22957                 inputblock.cn.push({
22958                     tag :'span',
22959                     cls : 'input-group-addon',
22960                     html : this.after
22961                 });
22962             }
22963             
22964         }
22965         var boxLabelCfg = false;
22966         
22967         if(this.boxLabel){
22968            
22969             boxLabelCfg = {
22970                 tag: 'label',
22971                 //'for': id, // box label is handled by onclick - so no for...
22972                 cls: 'box-label',
22973                 html: this.boxLabel
22974             };
22975             if(this.tooltip){
22976                 boxLabelCfg.tooltip = this.tooltip;
22977             }
22978              
22979         }
22980         
22981         
22982         if (align ==='left' && this.fieldLabel.length) {
22983 //                Roo.log("left and has label");
22984             cfg.cn = [
22985                 {
22986                     tag: 'label',
22987                     'for' :  id,
22988                     cls : 'control-label',
22989                     html : this.fieldLabel
22990                 },
22991                 {
22992                     cls : "", 
22993                     cn: [
22994                         inputblock
22995                     ]
22996                 }
22997             ];
22998             
22999             if (boxLabelCfg) {
23000                 cfg.cn[1].cn.push(boxLabelCfg);
23001             }
23002             
23003             if(this.labelWidth > 12){
23004                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23005             }
23006             
23007             if(this.labelWidth < 13 && this.labelmd == 0){
23008                 this.labelmd = this.labelWidth;
23009             }
23010             
23011             if(this.labellg > 0){
23012                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23013                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23014             }
23015             
23016             if(this.labelmd > 0){
23017                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23018                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23019             }
23020             
23021             if(this.labelsm > 0){
23022                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23023                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23024             }
23025             
23026             if(this.labelxs > 0){
23027                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23028                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23029             }
23030             
23031         } else if ( this.fieldLabel.length) {
23032 //                Roo.log(" label");
23033                 cfg.cn = [
23034                    
23035                     {
23036                         tag: this.boxLabel ? 'span' : 'label',
23037                         'for': id,
23038                         cls: 'control-label box-input-label',
23039                         //cls : 'input-group-addon',
23040                         html : this.fieldLabel
23041                     },
23042                     
23043                     inputblock
23044                     
23045                 ];
23046                 if (boxLabelCfg) {
23047                     cfg.cn.push(boxLabelCfg);
23048                 }
23049
23050         } else {
23051             
23052 //                Roo.log(" no label && no align");
23053                 cfg.cn = [  inputblock ] ;
23054                 if (boxLabelCfg) {
23055                     cfg.cn.push(boxLabelCfg);
23056                 }
23057
23058                 
23059         }
23060         
23061        
23062         
23063         if(this.inputType != 'radio'){
23064             cfg.cn.push(hidden);
23065         }
23066         
23067         return cfg;
23068         
23069     },
23070     
23071     /**
23072      * return the real input element.
23073      */
23074     inputEl: function ()
23075     {
23076         return this.el.select('input.roo-' + this.inputType,true).first();
23077     },
23078     hiddenEl: function ()
23079     {
23080         return this.el.select('input.roo-hidden-value',true).first();
23081     },
23082     
23083     labelEl: function()
23084     {
23085         return this.el.select('label.control-label',true).first();
23086     },
23087     /* depricated... */
23088     
23089     label: function()
23090     {
23091         return this.labelEl();
23092     },
23093     
23094     boxLabelEl: function()
23095     {
23096         return this.el.select('label.box-label',true).first();
23097     },
23098     
23099     initEvents : function()
23100     {
23101 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23102         
23103         this.inputEl().on('click', this.onClick,  this);
23104         
23105         if (this.boxLabel) { 
23106             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23107         }
23108         
23109         this.startValue = this.getValue();
23110         
23111         if(this.groupId){
23112             Roo.bootstrap.CheckBox.register(this);
23113         }
23114     },
23115     
23116     onClick : function(e)
23117     {   
23118         if(this.fireEvent('click', this, e) !== false){
23119             this.setChecked(!this.checked);
23120         }
23121         
23122     },
23123     
23124     setChecked : function(state,suppressEvent)
23125     {
23126         this.startValue = this.getValue();
23127
23128         if(this.inputType == 'radio'){
23129             
23130             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23131                 e.dom.checked = false;
23132             });
23133             
23134             this.inputEl().dom.checked = true;
23135             
23136             this.inputEl().dom.value = this.inputValue;
23137             
23138             if(suppressEvent !== true){
23139                 this.fireEvent('check', this, true);
23140             }
23141             
23142             this.validate();
23143             
23144             return;
23145         }
23146         
23147         this.checked = state;
23148         
23149         this.inputEl().dom.checked = state;
23150         
23151         
23152         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23153         
23154         if(suppressEvent !== true){
23155             this.fireEvent('check', this, state);
23156         }
23157         
23158         this.validate();
23159     },
23160     
23161     getValue : function()
23162     {
23163         if(this.inputType == 'radio'){
23164             return this.getGroupValue();
23165         }
23166         
23167         return this.hiddenEl().dom.value;
23168         
23169     },
23170     
23171     getGroupValue : function()
23172     {
23173         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23174             return '';
23175         }
23176         
23177         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23178     },
23179     
23180     setValue : function(v,suppressEvent)
23181     {
23182         if(this.inputType == 'radio'){
23183             this.setGroupValue(v, suppressEvent);
23184             return;
23185         }
23186         
23187         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23188         
23189         this.validate();
23190     },
23191     
23192     setGroupValue : function(v, suppressEvent)
23193     {
23194         this.startValue = this.getValue();
23195         
23196         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23197             e.dom.checked = false;
23198             
23199             if(e.dom.value == v){
23200                 e.dom.checked = true;
23201             }
23202         });
23203         
23204         if(suppressEvent !== true){
23205             this.fireEvent('check', this, true);
23206         }
23207
23208         this.validate();
23209         
23210         return;
23211     },
23212     
23213     validate : function()
23214     {
23215         if(this.getVisibilityEl().hasClass('hidden')){
23216             return true;
23217         }
23218         
23219         if(
23220                 this.disabled || 
23221                 (this.inputType == 'radio' && this.validateRadio()) ||
23222                 (this.inputType == 'checkbox' && this.validateCheckbox())
23223         ){
23224             this.markValid();
23225             return true;
23226         }
23227         
23228         this.markInvalid();
23229         return false;
23230     },
23231     
23232     validateRadio : function()
23233     {
23234         if(this.getVisibilityEl().hasClass('hidden')){
23235             return true;
23236         }
23237         
23238         if(this.allowBlank){
23239             return true;
23240         }
23241         
23242         var valid = false;
23243         
23244         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23245             if(!e.dom.checked){
23246                 return;
23247             }
23248             
23249             valid = true;
23250             
23251             return false;
23252         });
23253         
23254         return valid;
23255     },
23256     
23257     validateCheckbox : function()
23258     {
23259         if(!this.groupId){
23260             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23261             //return (this.getValue() == this.inputValue) ? true : false;
23262         }
23263         
23264         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23265         
23266         if(!group){
23267             return false;
23268         }
23269         
23270         var r = false;
23271         
23272         for(var i in group){
23273             if(group[i].el.isVisible(true)){
23274                 r = false;
23275                 break;
23276             }
23277             
23278             r = true;
23279         }
23280         
23281         for(var i in group){
23282             if(r){
23283                 break;
23284             }
23285             
23286             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23287         }
23288         
23289         return r;
23290     },
23291     
23292     /**
23293      * Mark this field as valid
23294      */
23295     markValid : function()
23296     {
23297         var _this = this;
23298         
23299         this.fireEvent('valid', this);
23300         
23301         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23302         
23303         if(this.groupId){
23304             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23305         }
23306         
23307         if(label){
23308             label.markValid();
23309         }
23310
23311         if(this.inputType == 'radio'){
23312             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23313                 var fg = e.findParent('.form-group', false, true);
23314                 if (Roo.bootstrap.version == 3) {
23315                     fg.removeClass([_this.invalidClass, _this.validClass]);
23316                     fg.addClass(_this.validClass);
23317                 } else {
23318                     fg.removeClass(['is-valid', 'is-invalid']);
23319                     fg.addClass('is-valid');
23320                 }
23321             });
23322             
23323             return;
23324         }
23325
23326         if(!this.groupId){
23327             var fg = this.el.findParent('.form-group', false, true);
23328             if (Roo.bootstrap.version == 3) {
23329                 fg.removeClass([this.invalidClass, this.validClass]);
23330                 fg.addClass(this.validClass);
23331             } else {
23332                 fg.removeClass(['is-valid', 'is-invalid']);
23333                 fg.addClass('is-valid');
23334             }
23335             return;
23336         }
23337         
23338         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23339         
23340         if(!group){
23341             return;
23342         }
23343         
23344         for(var i in group){
23345             var fg = group[i].el.findParent('.form-group', false, true);
23346             if (Roo.bootstrap.version == 3) {
23347                 fg.removeClass([this.invalidClass, this.validClass]);
23348                 fg.addClass(this.validClass);
23349             } else {
23350                 fg.removeClass(['is-valid', 'is-invalid']);
23351                 fg.addClass('is-valid');
23352             }
23353         }
23354     },
23355     
23356      /**
23357      * Mark this field as invalid
23358      * @param {String} msg The validation message
23359      */
23360     markInvalid : function(msg)
23361     {
23362         if(this.allowBlank){
23363             return;
23364         }
23365         
23366         var _this = this;
23367         
23368         this.fireEvent('invalid', this, msg);
23369         
23370         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23371         
23372         if(this.groupId){
23373             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23374         }
23375         
23376         if(label){
23377             label.markInvalid();
23378         }
23379             
23380         if(this.inputType == 'radio'){
23381             
23382             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23383                 var fg = e.findParent('.form-group', false, true);
23384                 if (Roo.bootstrap.version == 3) {
23385                     fg.removeClass([_this.invalidClass, _this.validClass]);
23386                     fg.addClass(_this.invalidClass);
23387                 } else {
23388                     fg.removeClass(['is-invalid', 'is-valid']);
23389                     fg.addClass('is-invalid');
23390                 }
23391             });
23392             
23393             return;
23394         }
23395         
23396         if(!this.groupId){
23397             var fg = this.el.findParent('.form-group', false, true);
23398             if (Roo.bootstrap.version == 3) {
23399                 fg.removeClass([_this.invalidClass, _this.validClass]);
23400                 fg.addClass(_this.invalidClass);
23401             } else {
23402                 fg.removeClass(['is-invalid', 'is-valid']);
23403                 fg.addClass('is-invalid');
23404             }
23405             return;
23406         }
23407         
23408         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23409         
23410         if(!group){
23411             return;
23412         }
23413         
23414         for(var i in group){
23415             var fg = group[i].el.findParent('.form-group', false, true);
23416             if (Roo.bootstrap.version == 3) {
23417                 fg.removeClass([_this.invalidClass, _this.validClass]);
23418                 fg.addClass(_this.invalidClass);
23419             } else {
23420                 fg.removeClass(['is-invalid', 'is-valid']);
23421                 fg.addClass('is-invalid');
23422             }
23423         }
23424         
23425     },
23426     
23427     clearInvalid : function()
23428     {
23429         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23430         
23431         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23432         
23433         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23434         
23435         if (label && label.iconEl) {
23436             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23437             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23438         }
23439     },
23440     
23441     disable : function()
23442     {
23443         if(this.inputType != 'radio'){
23444             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23445             return;
23446         }
23447         
23448         var _this = this;
23449         
23450         if(this.rendered){
23451             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23452                 _this.getActionEl().addClass(this.disabledClass);
23453                 e.dom.disabled = true;
23454             });
23455         }
23456         
23457         this.disabled = true;
23458         this.fireEvent("disable", this);
23459         return this;
23460     },
23461
23462     enable : function()
23463     {
23464         if(this.inputType != 'radio'){
23465             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23466             return;
23467         }
23468         
23469         var _this = this;
23470         
23471         if(this.rendered){
23472             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23473                 _this.getActionEl().removeClass(this.disabledClass);
23474                 e.dom.disabled = false;
23475             });
23476         }
23477         
23478         this.disabled = false;
23479         this.fireEvent("enable", this);
23480         return this;
23481     },
23482     
23483     setBoxLabel : function(v)
23484     {
23485         this.boxLabel = v;
23486         
23487         if(this.rendered){
23488             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23489         }
23490     }
23491
23492 });
23493
23494 Roo.apply(Roo.bootstrap.CheckBox, {
23495     
23496     groups: {},
23497     
23498      /**
23499     * register a CheckBox Group
23500     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23501     */
23502     register : function(checkbox)
23503     {
23504         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23505             this.groups[checkbox.groupId] = {};
23506         }
23507         
23508         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23509             return;
23510         }
23511         
23512         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23513         
23514     },
23515     /**
23516     * fetch a CheckBox Group based on the group ID
23517     * @param {string} the group ID
23518     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23519     */
23520     get: function(groupId) {
23521         if (typeof(this.groups[groupId]) == 'undefined') {
23522             return false;
23523         }
23524         
23525         return this.groups[groupId] ;
23526     }
23527     
23528     
23529 });
23530 /*
23531  * - LGPL
23532  *
23533  * RadioItem
23534  * 
23535  */
23536
23537 /**
23538  * @class Roo.bootstrap.Radio
23539  * @extends Roo.bootstrap.Component
23540  * Bootstrap Radio class
23541  * @cfg {String} boxLabel - the label associated
23542  * @cfg {String} value - the value of radio
23543  * 
23544  * @constructor
23545  * Create a new Radio
23546  * @param {Object} config The config object
23547  */
23548 Roo.bootstrap.Radio = function(config){
23549     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23550     
23551 };
23552
23553 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23554     
23555     boxLabel : '',
23556     
23557     value : '',
23558     
23559     getAutoCreate : function()
23560     {
23561         var cfg = {
23562             tag : 'div',
23563             cls : 'form-group radio',
23564             cn : [
23565                 {
23566                     tag : 'label',
23567                     cls : 'box-label',
23568                     html : this.boxLabel
23569                 }
23570             ]
23571         };
23572         
23573         return cfg;
23574     },
23575     
23576     initEvents : function() 
23577     {
23578         this.parent().register(this);
23579         
23580         this.el.on('click', this.onClick, this);
23581         
23582     },
23583     
23584     onClick : function(e)
23585     {
23586         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23587             this.setChecked(true);
23588         }
23589     },
23590     
23591     setChecked : function(state, suppressEvent)
23592     {
23593         this.parent().setValue(this.value, suppressEvent);
23594         
23595     },
23596     
23597     setBoxLabel : function(v)
23598     {
23599         this.boxLabel = v;
23600         
23601         if(this.rendered){
23602             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23603         }
23604     }
23605     
23606 });
23607  
23608
23609  /*
23610  * - LGPL
23611  *
23612  * Input
23613  * 
23614  */
23615
23616 /**
23617  * @class Roo.bootstrap.SecurePass
23618  * @extends Roo.bootstrap.Input
23619  * Bootstrap SecurePass class
23620  *
23621  * 
23622  * @constructor
23623  * Create a new SecurePass
23624  * @param {Object} config The config object
23625  */
23626  
23627 Roo.bootstrap.SecurePass = function (config) {
23628     // these go here, so the translation tool can replace them..
23629     this.errors = {
23630         PwdEmpty: "Please type a password, and then retype it to confirm.",
23631         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23632         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23633         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23634         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23635         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23636         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23637         TooWeak: "Your password is Too Weak."
23638     },
23639     this.meterLabel = "Password strength:";
23640     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23641     this.meterClass = [
23642         "roo-password-meter-tooweak", 
23643         "roo-password-meter-weak", 
23644         "roo-password-meter-medium", 
23645         "roo-password-meter-strong", 
23646         "roo-password-meter-grey"
23647     ];
23648     
23649     this.errors = {};
23650     
23651     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23652 }
23653
23654 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23655     /**
23656      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23657      * {
23658      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23659      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23660      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23661      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23662      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23663      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23664      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23665      * })
23666      */
23667     // private
23668     
23669     meterWidth: 300,
23670     errorMsg :'',    
23671     errors: false,
23672     imageRoot: '/',
23673     /**
23674      * @cfg {String/Object} Label for the strength meter (defaults to
23675      * 'Password strength:')
23676      */
23677     // private
23678     meterLabel: '',
23679     /**
23680      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23681      * ['Weak', 'Medium', 'Strong'])
23682      */
23683     // private    
23684     pwdStrengths: false,    
23685     // private
23686     strength: 0,
23687     // private
23688     _lastPwd: null,
23689     // private
23690     kCapitalLetter: 0,
23691     kSmallLetter: 1,
23692     kDigit: 2,
23693     kPunctuation: 3,
23694     
23695     insecure: false,
23696     // private
23697     initEvents: function ()
23698     {
23699         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23700
23701         if (this.el.is('input[type=password]') && Roo.isSafari) {
23702             this.el.on('keydown', this.SafariOnKeyDown, this);
23703         }
23704
23705         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23706     },
23707     // private
23708     onRender: function (ct, position)
23709     {
23710         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23711         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23712         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23713
23714         this.trigger.createChild({
23715                    cn: [
23716                     {
23717                     //id: 'PwdMeter',
23718                     tag: 'div',
23719                     cls: 'roo-password-meter-grey col-xs-12',
23720                     style: {
23721                         //width: 0,
23722                         //width: this.meterWidth + 'px'                                                
23723                         }
23724                     },
23725                     {                            
23726                          cls: 'roo-password-meter-text'                          
23727                     }
23728                 ]            
23729         });
23730
23731          
23732         if (this.hideTrigger) {
23733             this.trigger.setDisplayed(false);
23734         }
23735         this.setSize(this.width || '', this.height || '');
23736     },
23737     // private
23738     onDestroy: function ()
23739     {
23740         if (this.trigger) {
23741             this.trigger.removeAllListeners();
23742             this.trigger.remove();
23743         }
23744         if (this.wrap) {
23745             this.wrap.remove();
23746         }
23747         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23748     },
23749     // private
23750     checkStrength: function ()
23751     {
23752         var pwd = this.inputEl().getValue();
23753         if (pwd == this._lastPwd) {
23754             return;
23755         }
23756
23757         var strength;
23758         if (this.ClientSideStrongPassword(pwd)) {
23759             strength = 3;
23760         } else if (this.ClientSideMediumPassword(pwd)) {
23761             strength = 2;
23762         } else if (this.ClientSideWeakPassword(pwd)) {
23763             strength = 1;
23764         } else {
23765             strength = 0;
23766         }
23767         
23768         Roo.log('strength1: ' + strength);
23769         
23770         //var pm = this.trigger.child('div/div/div').dom;
23771         var pm = this.trigger.child('div/div');
23772         pm.removeClass(this.meterClass);
23773         pm.addClass(this.meterClass[strength]);
23774                 
23775         
23776         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23777                 
23778         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23779         
23780         this._lastPwd = pwd;
23781     },
23782     reset: function ()
23783     {
23784         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23785         
23786         this._lastPwd = '';
23787         
23788         var pm = this.trigger.child('div/div');
23789         pm.removeClass(this.meterClass);
23790         pm.addClass('roo-password-meter-grey');        
23791         
23792         
23793         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23794         
23795         pt.innerHTML = '';
23796         this.inputEl().dom.type='password';
23797     },
23798     // private
23799     validateValue: function (value)
23800     {
23801         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23802             return false;
23803         }
23804         if (value.length == 0) {
23805             if (this.allowBlank) {
23806                 this.clearInvalid();
23807                 return true;
23808             }
23809
23810             this.markInvalid(this.errors.PwdEmpty);
23811             this.errorMsg = this.errors.PwdEmpty;
23812             return false;
23813         }
23814         
23815         if(this.insecure){
23816             return true;
23817         }
23818         
23819         if (!value.match(/[\x21-\x7e]+/)) {
23820             this.markInvalid(this.errors.PwdBadChar);
23821             this.errorMsg = this.errors.PwdBadChar;
23822             return false;
23823         }
23824         if (value.length < 6) {
23825             this.markInvalid(this.errors.PwdShort);
23826             this.errorMsg = this.errors.PwdShort;
23827             return false;
23828         }
23829         if (value.length > 16) {
23830             this.markInvalid(this.errors.PwdLong);
23831             this.errorMsg = this.errors.PwdLong;
23832             return false;
23833         }
23834         var strength;
23835         if (this.ClientSideStrongPassword(value)) {
23836             strength = 3;
23837         } else if (this.ClientSideMediumPassword(value)) {
23838             strength = 2;
23839         } else if (this.ClientSideWeakPassword(value)) {
23840             strength = 1;
23841         } else {
23842             strength = 0;
23843         }
23844
23845         
23846         if (strength < 2) {
23847             //this.markInvalid(this.errors.TooWeak);
23848             this.errorMsg = this.errors.TooWeak;
23849             //return false;
23850         }
23851         
23852         
23853         console.log('strength2: ' + strength);
23854         
23855         //var pm = this.trigger.child('div/div/div').dom;
23856         
23857         var pm = this.trigger.child('div/div');
23858         pm.removeClass(this.meterClass);
23859         pm.addClass(this.meterClass[strength]);
23860                 
23861         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23862                 
23863         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23864         
23865         this.errorMsg = ''; 
23866         return true;
23867     },
23868     // private
23869     CharacterSetChecks: function (type)
23870     {
23871         this.type = type;
23872         this.fResult = false;
23873     },
23874     // private
23875     isctype: function (character, type)
23876     {
23877         switch (type) {  
23878             case this.kCapitalLetter:
23879                 if (character >= 'A' && character <= 'Z') {
23880                     return true;
23881                 }
23882                 break;
23883             
23884             case this.kSmallLetter:
23885                 if (character >= 'a' && character <= 'z') {
23886                     return true;
23887                 }
23888                 break;
23889             
23890             case this.kDigit:
23891                 if (character >= '0' && character <= '9') {
23892                     return true;
23893                 }
23894                 break;
23895             
23896             case this.kPunctuation:
23897                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23898                     return true;
23899                 }
23900                 break;
23901             
23902             default:
23903                 return false;
23904         }
23905
23906     },
23907     // private
23908     IsLongEnough: function (pwd, size)
23909     {
23910         return !(pwd == null || isNaN(size) || pwd.length < size);
23911     },
23912     // private
23913     SpansEnoughCharacterSets: function (word, nb)
23914     {
23915         if (!this.IsLongEnough(word, nb))
23916         {
23917             return false;
23918         }
23919
23920         var characterSetChecks = new Array(
23921             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23922             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23923         );
23924         
23925         for (var index = 0; index < word.length; ++index) {
23926             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23927                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23928                     characterSetChecks[nCharSet].fResult = true;
23929                     break;
23930                 }
23931             }
23932         }
23933
23934         var nCharSets = 0;
23935         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23936             if (characterSetChecks[nCharSet].fResult) {
23937                 ++nCharSets;
23938             }
23939         }
23940
23941         if (nCharSets < nb) {
23942             return false;
23943         }
23944         return true;
23945     },
23946     // private
23947     ClientSideStrongPassword: function (pwd)
23948     {
23949         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23950     },
23951     // private
23952     ClientSideMediumPassword: function (pwd)
23953     {
23954         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23955     },
23956     // private
23957     ClientSideWeakPassword: function (pwd)
23958     {
23959         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23960     }
23961           
23962 })//<script type="text/javascript">
23963
23964 /*
23965  * Based  Ext JS Library 1.1.1
23966  * Copyright(c) 2006-2007, Ext JS, LLC.
23967  * LGPL
23968  *
23969  */
23970  
23971 /**
23972  * @class Roo.HtmlEditorCore
23973  * @extends Roo.Component
23974  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23975  *
23976  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23977  */
23978
23979 Roo.HtmlEditorCore = function(config){
23980     
23981     
23982     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23983     
23984     
23985     this.addEvents({
23986         /**
23987          * @event initialize
23988          * Fires when the editor is fully initialized (including the iframe)
23989          * @param {Roo.HtmlEditorCore} this
23990          */
23991         initialize: true,
23992         /**
23993          * @event activate
23994          * Fires when the editor is first receives the focus. Any insertion must wait
23995          * until after this event.
23996          * @param {Roo.HtmlEditorCore} this
23997          */
23998         activate: true,
23999          /**
24000          * @event beforesync
24001          * Fires before the textarea is updated with content from the editor iframe. Return false
24002          * to cancel the sync.
24003          * @param {Roo.HtmlEditorCore} this
24004          * @param {String} html
24005          */
24006         beforesync: true,
24007          /**
24008          * @event beforepush
24009          * Fires before the iframe editor is updated with content from the textarea. Return false
24010          * to cancel the push.
24011          * @param {Roo.HtmlEditorCore} this
24012          * @param {String} html
24013          */
24014         beforepush: true,
24015          /**
24016          * @event sync
24017          * Fires when the textarea is updated with content from the editor iframe.
24018          * @param {Roo.HtmlEditorCore} this
24019          * @param {String} html
24020          */
24021         sync: true,
24022          /**
24023          * @event push
24024          * Fires when the iframe editor is updated with content from the textarea.
24025          * @param {Roo.HtmlEditorCore} this
24026          * @param {String} html
24027          */
24028         push: true,
24029         
24030         /**
24031          * @event editorevent
24032          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24033          * @param {Roo.HtmlEditorCore} this
24034          */
24035         editorevent: true
24036         
24037     });
24038     
24039     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24040     
24041     // defaults : white / black...
24042     this.applyBlacklists();
24043     
24044     
24045     
24046 };
24047
24048
24049 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24050
24051
24052      /**
24053      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24054      */
24055     
24056     owner : false,
24057     
24058      /**
24059      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24060      *                        Roo.resizable.
24061      */
24062     resizable : false,
24063      /**
24064      * @cfg {Number} height (in pixels)
24065      */   
24066     height: 300,
24067    /**
24068      * @cfg {Number} width (in pixels)
24069      */   
24070     width: 500,
24071     
24072     /**
24073      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24074      * 
24075      */
24076     stylesheets: false,
24077     
24078     // id of frame..
24079     frameId: false,
24080     
24081     // private properties
24082     validationEvent : false,
24083     deferHeight: true,
24084     initialized : false,
24085     activated : false,
24086     sourceEditMode : false,
24087     onFocus : Roo.emptyFn,
24088     iframePad:3,
24089     hideMode:'offsets',
24090     
24091     clearUp: true,
24092     
24093     // blacklist + whitelisted elements..
24094     black: false,
24095     white: false,
24096      
24097     bodyCls : '',
24098
24099     /**
24100      * Protected method that will not generally be called directly. It
24101      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24102      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24103      */
24104     getDocMarkup : function(){
24105         // body styles..
24106         var st = '';
24107         
24108         // inherit styels from page...?? 
24109         if (this.stylesheets === false) {
24110             
24111             Roo.get(document.head).select('style').each(function(node) {
24112                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24113             });
24114             
24115             Roo.get(document.head).select('link').each(function(node) { 
24116                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24117             });
24118             
24119         } else if (!this.stylesheets.length) {
24120                 // simple..
24121                 st = '<style type="text/css">' +
24122                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24123                    '</style>';
24124         } else {
24125             for (var i in this.stylesheets) { 
24126                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24127             }
24128             
24129         }
24130         
24131         st +=  '<style type="text/css">' +
24132             'IMG { cursor: pointer } ' +
24133         '</style>';
24134
24135         var cls = 'roo-htmleditor-body';
24136         
24137         if(this.bodyCls.length){
24138             cls += ' ' + this.bodyCls;
24139         }
24140         
24141         return '<html><head>' + st  +
24142             //<style type="text/css">' +
24143             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24144             //'</style>' +
24145             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24146     },
24147
24148     // private
24149     onRender : function(ct, position)
24150     {
24151         var _t = this;
24152         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24153         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24154         
24155         
24156         this.el.dom.style.border = '0 none';
24157         this.el.dom.setAttribute('tabIndex', -1);
24158         this.el.addClass('x-hidden hide');
24159         
24160         
24161         
24162         if(Roo.isIE){ // fix IE 1px bogus margin
24163             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24164         }
24165        
24166         
24167         this.frameId = Roo.id();
24168         
24169          
24170         
24171         var iframe = this.owner.wrap.createChild({
24172             tag: 'iframe',
24173             cls: 'form-control', // bootstrap..
24174             id: this.frameId,
24175             name: this.frameId,
24176             frameBorder : 'no',
24177             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24178         }, this.el
24179         );
24180         
24181         
24182         this.iframe = iframe.dom;
24183
24184          this.assignDocWin();
24185         
24186         this.doc.designMode = 'on';
24187        
24188         this.doc.open();
24189         this.doc.write(this.getDocMarkup());
24190         this.doc.close();
24191
24192         
24193         var task = { // must defer to wait for browser to be ready
24194             run : function(){
24195                 //console.log("run task?" + this.doc.readyState);
24196                 this.assignDocWin();
24197                 if(this.doc.body || this.doc.readyState == 'complete'){
24198                     try {
24199                         this.doc.designMode="on";
24200                     } catch (e) {
24201                         return;
24202                     }
24203                     Roo.TaskMgr.stop(task);
24204                     this.initEditor.defer(10, this);
24205                 }
24206             },
24207             interval : 10,
24208             duration: 10000,
24209             scope: this
24210         };
24211         Roo.TaskMgr.start(task);
24212
24213     },
24214
24215     // private
24216     onResize : function(w, h)
24217     {
24218          Roo.log('resize: ' +w + ',' + h );
24219         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24220         if(!this.iframe){
24221             return;
24222         }
24223         if(typeof w == 'number'){
24224             
24225             this.iframe.style.width = w + 'px';
24226         }
24227         if(typeof h == 'number'){
24228             
24229             this.iframe.style.height = h + 'px';
24230             if(this.doc){
24231                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24232             }
24233         }
24234         
24235     },
24236
24237     /**
24238      * Toggles the editor between standard and source edit mode.
24239      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24240      */
24241     toggleSourceEdit : function(sourceEditMode){
24242         
24243         this.sourceEditMode = sourceEditMode === true;
24244         
24245         if(this.sourceEditMode){
24246  
24247             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24248             
24249         }else{
24250             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24251             //this.iframe.className = '';
24252             this.deferFocus();
24253         }
24254         //this.setSize(this.owner.wrap.getSize());
24255         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24256     },
24257
24258     
24259   
24260
24261     /**
24262      * Protected method that will not generally be called directly. If you need/want
24263      * custom HTML cleanup, this is the method you should override.
24264      * @param {String} html The HTML to be cleaned
24265      * return {String} The cleaned HTML
24266      */
24267     cleanHtml : function(html){
24268         html = String(html);
24269         if(html.length > 5){
24270             if(Roo.isSafari){ // strip safari nonsense
24271                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24272             }
24273         }
24274         if(html == '&nbsp;'){
24275             html = '';
24276         }
24277         return html;
24278     },
24279
24280     /**
24281      * HTML Editor -> Textarea
24282      * Protected method that will not generally be called directly. Syncs the contents
24283      * of the editor iframe with the textarea.
24284      */
24285     syncValue : function(){
24286         if(this.initialized){
24287             var bd = (this.doc.body || this.doc.documentElement);
24288             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24289             var html = bd.innerHTML;
24290             if(Roo.isSafari){
24291                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24292                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24293                 if(m && m[1]){
24294                     html = '<div style="'+m[0]+'">' + html + '</div>';
24295                 }
24296             }
24297             html = this.cleanHtml(html);
24298             // fix up the special chars.. normaly like back quotes in word...
24299             // however we do not want to do this with chinese..
24300             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24301                 
24302                 var cc = match.charCodeAt();
24303
24304                 // Get the character value, handling surrogate pairs
24305                 if (match.length == 2) {
24306                     // It's a surrogate pair, calculate the Unicode code point
24307                     var high = match.charCodeAt(0) - 0xD800;
24308                     var low  = match.charCodeAt(1) - 0xDC00;
24309                     cc = (high * 0x400) + low + 0x10000;
24310                 }  else if (
24311                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24312                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24313                     (cc >= 0xf900 && cc < 0xfb00 )
24314                 ) {
24315                         return match;
24316                 }  
24317          
24318                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24319                 return "&#" + cc + ";";
24320                 
24321                 
24322             });
24323             
24324             
24325              
24326             if(this.owner.fireEvent('beforesync', this, html) !== false){
24327                 this.el.dom.value = html;
24328                 this.owner.fireEvent('sync', this, html);
24329             }
24330         }
24331     },
24332
24333     /**
24334      * Protected method that will not generally be called directly. Pushes the value of the textarea
24335      * into the iframe editor.
24336      */
24337     pushValue : function(){
24338         if(this.initialized){
24339             var v = this.el.dom.value.trim();
24340             
24341 //            if(v.length < 1){
24342 //                v = '&#160;';
24343 //            }
24344             
24345             if(this.owner.fireEvent('beforepush', this, v) !== false){
24346                 var d = (this.doc.body || this.doc.documentElement);
24347                 d.innerHTML = v;
24348                 this.cleanUpPaste();
24349                 this.el.dom.value = d.innerHTML;
24350                 this.owner.fireEvent('push', this, v);
24351             }
24352         }
24353     },
24354
24355     // private
24356     deferFocus : function(){
24357         this.focus.defer(10, this);
24358     },
24359
24360     // doc'ed in Field
24361     focus : function(){
24362         if(this.win && !this.sourceEditMode){
24363             this.win.focus();
24364         }else{
24365             this.el.focus();
24366         }
24367     },
24368     
24369     assignDocWin: function()
24370     {
24371         var iframe = this.iframe;
24372         
24373          if(Roo.isIE){
24374             this.doc = iframe.contentWindow.document;
24375             this.win = iframe.contentWindow;
24376         } else {
24377 //            if (!Roo.get(this.frameId)) {
24378 //                return;
24379 //            }
24380 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24381 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24382             
24383             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24384                 return;
24385             }
24386             
24387             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24388             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24389         }
24390     },
24391     
24392     // private
24393     initEditor : function(){
24394         //console.log("INIT EDITOR");
24395         this.assignDocWin();
24396         
24397         
24398         
24399         this.doc.designMode="on";
24400         this.doc.open();
24401         this.doc.write(this.getDocMarkup());
24402         this.doc.close();
24403         
24404         var dbody = (this.doc.body || this.doc.documentElement);
24405         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24406         // this copies styles from the containing element into thsi one..
24407         // not sure why we need all of this..
24408         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24409         
24410         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24411         //ss['background-attachment'] = 'fixed'; // w3c
24412         dbody.bgProperties = 'fixed'; // ie
24413         //Roo.DomHelper.applyStyles(dbody, ss);
24414         Roo.EventManager.on(this.doc, {
24415             //'mousedown': this.onEditorEvent,
24416             'mouseup': this.onEditorEvent,
24417             'dblclick': this.onEditorEvent,
24418             'click': this.onEditorEvent,
24419             'keyup': this.onEditorEvent,
24420             buffer:100,
24421             scope: this
24422         });
24423         if(Roo.isGecko){
24424             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24425         }
24426         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24427             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24428         }
24429         this.initialized = true;
24430
24431         this.owner.fireEvent('initialize', this);
24432         this.pushValue();
24433     },
24434
24435     // private
24436     onDestroy : function(){
24437         
24438         
24439         
24440         if(this.rendered){
24441             
24442             //for (var i =0; i < this.toolbars.length;i++) {
24443             //    // fixme - ask toolbars for heights?
24444             //    this.toolbars[i].onDestroy();
24445            // }
24446             
24447             //this.wrap.dom.innerHTML = '';
24448             //this.wrap.remove();
24449         }
24450     },
24451
24452     // private
24453     onFirstFocus : function(){
24454         
24455         this.assignDocWin();
24456         
24457         
24458         this.activated = true;
24459          
24460     
24461         if(Roo.isGecko){ // prevent silly gecko errors
24462             this.win.focus();
24463             var s = this.win.getSelection();
24464             if(!s.focusNode || s.focusNode.nodeType != 3){
24465                 var r = s.getRangeAt(0);
24466                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24467                 r.collapse(true);
24468                 this.deferFocus();
24469             }
24470             try{
24471                 this.execCmd('useCSS', true);
24472                 this.execCmd('styleWithCSS', false);
24473             }catch(e){}
24474         }
24475         this.owner.fireEvent('activate', this);
24476     },
24477
24478     // private
24479     adjustFont: function(btn){
24480         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24481         //if(Roo.isSafari){ // safari
24482         //    adjust *= 2;
24483        // }
24484         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24485         if(Roo.isSafari){ // safari
24486             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24487             v =  (v < 10) ? 10 : v;
24488             v =  (v > 48) ? 48 : v;
24489             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24490             
24491         }
24492         
24493         
24494         v = Math.max(1, v+adjust);
24495         
24496         this.execCmd('FontSize', v  );
24497     },
24498
24499     onEditorEvent : function(e)
24500     {
24501         this.owner.fireEvent('editorevent', this, e);
24502       //  this.updateToolbar();
24503         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24504     },
24505
24506     insertTag : function(tg)
24507     {
24508         // could be a bit smarter... -> wrap the current selected tRoo..
24509         if (tg.toLowerCase() == 'span' ||
24510             tg.toLowerCase() == 'code' ||
24511             tg.toLowerCase() == 'sup' ||
24512             tg.toLowerCase() == 'sub' 
24513             ) {
24514             
24515             range = this.createRange(this.getSelection());
24516             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24517             wrappingNode.appendChild(range.extractContents());
24518             range.insertNode(wrappingNode);
24519
24520             return;
24521             
24522             
24523             
24524         }
24525         this.execCmd("formatblock",   tg);
24526         
24527     },
24528     
24529     insertText : function(txt)
24530     {
24531         
24532         
24533         var range = this.createRange();
24534         range.deleteContents();
24535                //alert(Sender.getAttribute('label'));
24536                
24537         range.insertNode(this.doc.createTextNode(txt));
24538     } ,
24539     
24540      
24541
24542     /**
24543      * Executes a Midas editor command on the editor document and performs necessary focus and
24544      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24545      * @param {String} cmd The Midas command
24546      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24547      */
24548     relayCmd : function(cmd, value){
24549         this.win.focus();
24550         this.execCmd(cmd, value);
24551         this.owner.fireEvent('editorevent', this);
24552         //this.updateToolbar();
24553         this.owner.deferFocus();
24554     },
24555
24556     /**
24557      * Executes a Midas editor command directly on the editor document.
24558      * For visual commands, you should use {@link #relayCmd} instead.
24559      * <b>This should only be called after the editor is initialized.</b>
24560      * @param {String} cmd The Midas command
24561      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24562      */
24563     execCmd : function(cmd, value){
24564         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24565         this.syncValue();
24566     },
24567  
24568  
24569    
24570     /**
24571      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24572      * to insert tRoo.
24573      * @param {String} text | dom node.. 
24574      */
24575     insertAtCursor : function(text)
24576     {
24577         
24578         if(!this.activated){
24579             return;
24580         }
24581         /*
24582         if(Roo.isIE){
24583             this.win.focus();
24584             var r = this.doc.selection.createRange();
24585             if(r){
24586                 r.collapse(true);
24587                 r.pasteHTML(text);
24588                 this.syncValue();
24589                 this.deferFocus();
24590             
24591             }
24592             return;
24593         }
24594         */
24595         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24596             this.win.focus();
24597             
24598             
24599             // from jquery ui (MIT licenced)
24600             var range, node;
24601             var win = this.win;
24602             
24603             if (win.getSelection && win.getSelection().getRangeAt) {
24604                 range = win.getSelection().getRangeAt(0);
24605                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24606                 range.insertNode(node);
24607             } else if (win.document.selection && win.document.selection.createRange) {
24608                 // no firefox support
24609                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24610                 win.document.selection.createRange().pasteHTML(txt);
24611             } else {
24612                 // no firefox support
24613                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24614                 this.execCmd('InsertHTML', txt);
24615             } 
24616             
24617             this.syncValue();
24618             
24619             this.deferFocus();
24620         }
24621     },
24622  // private
24623     mozKeyPress : function(e){
24624         if(e.ctrlKey){
24625             var c = e.getCharCode(), cmd;
24626           
24627             if(c > 0){
24628                 c = String.fromCharCode(c).toLowerCase();
24629                 switch(c){
24630                     case 'b':
24631                         cmd = 'bold';
24632                         break;
24633                     case 'i':
24634                         cmd = 'italic';
24635                         break;
24636                     
24637                     case 'u':
24638                         cmd = 'underline';
24639                         break;
24640                     
24641                     case 'v':
24642                         this.cleanUpPaste.defer(100, this);
24643                         return;
24644                         
24645                 }
24646                 if(cmd){
24647                     this.win.focus();
24648                     this.execCmd(cmd);
24649                     this.deferFocus();
24650                     e.preventDefault();
24651                 }
24652                 
24653             }
24654         }
24655     },
24656
24657     // private
24658     fixKeys : function(){ // load time branching for fastest keydown performance
24659         if(Roo.isIE){
24660             return function(e){
24661                 var k = e.getKey(), r;
24662                 if(k == e.TAB){
24663                     e.stopEvent();
24664                     r = this.doc.selection.createRange();
24665                     if(r){
24666                         r.collapse(true);
24667                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24668                         this.deferFocus();
24669                     }
24670                     return;
24671                 }
24672                 
24673                 if(k == e.ENTER){
24674                     r = this.doc.selection.createRange();
24675                     if(r){
24676                         var target = r.parentElement();
24677                         if(!target || target.tagName.toLowerCase() != 'li'){
24678                             e.stopEvent();
24679                             r.pasteHTML('<br />');
24680                             r.collapse(false);
24681                             r.select();
24682                         }
24683                     }
24684                 }
24685                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24686                     this.cleanUpPaste.defer(100, this);
24687                     return;
24688                 }
24689                 
24690                 
24691             };
24692         }else if(Roo.isOpera){
24693             return function(e){
24694                 var k = e.getKey();
24695                 if(k == e.TAB){
24696                     e.stopEvent();
24697                     this.win.focus();
24698                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24699                     this.deferFocus();
24700                 }
24701                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24702                     this.cleanUpPaste.defer(100, this);
24703                     return;
24704                 }
24705                 
24706             };
24707         }else if(Roo.isSafari){
24708             return function(e){
24709                 var k = e.getKey();
24710                 
24711                 if(k == e.TAB){
24712                     e.stopEvent();
24713                     this.execCmd('InsertText','\t');
24714                     this.deferFocus();
24715                     return;
24716                 }
24717                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24718                     this.cleanUpPaste.defer(100, this);
24719                     return;
24720                 }
24721                 
24722              };
24723         }
24724     }(),
24725     
24726     getAllAncestors: function()
24727     {
24728         var p = this.getSelectedNode();
24729         var a = [];
24730         if (!p) {
24731             a.push(p); // push blank onto stack..
24732             p = this.getParentElement();
24733         }
24734         
24735         
24736         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24737             a.push(p);
24738             p = p.parentNode;
24739         }
24740         a.push(this.doc.body);
24741         return a;
24742     },
24743     lastSel : false,
24744     lastSelNode : false,
24745     
24746     
24747     getSelection : function() 
24748     {
24749         this.assignDocWin();
24750         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24751     },
24752     
24753     getSelectedNode: function() 
24754     {
24755         // this may only work on Gecko!!!
24756         
24757         // should we cache this!!!!
24758         
24759         
24760         
24761          
24762         var range = this.createRange(this.getSelection()).cloneRange();
24763         
24764         if (Roo.isIE) {
24765             var parent = range.parentElement();
24766             while (true) {
24767                 var testRange = range.duplicate();
24768                 testRange.moveToElementText(parent);
24769                 if (testRange.inRange(range)) {
24770                     break;
24771                 }
24772                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24773                     break;
24774                 }
24775                 parent = parent.parentElement;
24776             }
24777             return parent;
24778         }
24779         
24780         // is ancestor a text element.
24781         var ac =  range.commonAncestorContainer;
24782         if (ac.nodeType == 3) {
24783             ac = ac.parentNode;
24784         }
24785         
24786         var ar = ac.childNodes;
24787          
24788         var nodes = [];
24789         var other_nodes = [];
24790         var has_other_nodes = false;
24791         for (var i=0;i<ar.length;i++) {
24792             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24793                 continue;
24794             }
24795             // fullly contained node.
24796             
24797             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24798                 nodes.push(ar[i]);
24799                 continue;
24800             }
24801             
24802             // probably selected..
24803             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24804                 other_nodes.push(ar[i]);
24805                 continue;
24806             }
24807             // outer..
24808             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24809                 continue;
24810             }
24811             
24812             
24813             has_other_nodes = true;
24814         }
24815         if (!nodes.length && other_nodes.length) {
24816             nodes= other_nodes;
24817         }
24818         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24819             return false;
24820         }
24821         
24822         return nodes[0];
24823     },
24824     createRange: function(sel)
24825     {
24826         // this has strange effects when using with 
24827         // top toolbar - not sure if it's a great idea.
24828         //this.editor.contentWindow.focus();
24829         if (typeof sel != "undefined") {
24830             try {
24831                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24832             } catch(e) {
24833                 return this.doc.createRange();
24834             }
24835         } else {
24836             return this.doc.createRange();
24837         }
24838     },
24839     getParentElement: function()
24840     {
24841         
24842         this.assignDocWin();
24843         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24844         
24845         var range = this.createRange(sel);
24846          
24847         try {
24848             var p = range.commonAncestorContainer;
24849             while (p.nodeType == 3) { // text node
24850                 p = p.parentNode;
24851             }
24852             return p;
24853         } catch (e) {
24854             return null;
24855         }
24856     
24857     },
24858     /***
24859      *
24860      * Range intersection.. the hard stuff...
24861      *  '-1' = before
24862      *  '0' = hits..
24863      *  '1' = after.
24864      *         [ -- selected range --- ]
24865      *   [fail]                        [fail]
24866      *
24867      *    basically..
24868      *      if end is before start or  hits it. fail.
24869      *      if start is after end or hits it fail.
24870      *
24871      *   if either hits (but other is outside. - then it's not 
24872      *   
24873      *    
24874      **/
24875     
24876     
24877     // @see http://www.thismuchiknow.co.uk/?p=64.
24878     rangeIntersectsNode : function(range, node)
24879     {
24880         var nodeRange = node.ownerDocument.createRange();
24881         try {
24882             nodeRange.selectNode(node);
24883         } catch (e) {
24884             nodeRange.selectNodeContents(node);
24885         }
24886     
24887         var rangeStartRange = range.cloneRange();
24888         rangeStartRange.collapse(true);
24889     
24890         var rangeEndRange = range.cloneRange();
24891         rangeEndRange.collapse(false);
24892     
24893         var nodeStartRange = nodeRange.cloneRange();
24894         nodeStartRange.collapse(true);
24895     
24896         var nodeEndRange = nodeRange.cloneRange();
24897         nodeEndRange.collapse(false);
24898     
24899         return rangeStartRange.compareBoundaryPoints(
24900                  Range.START_TO_START, nodeEndRange) == -1 &&
24901                rangeEndRange.compareBoundaryPoints(
24902                  Range.START_TO_START, nodeStartRange) == 1;
24903         
24904          
24905     },
24906     rangeCompareNode : function(range, node)
24907     {
24908         var nodeRange = node.ownerDocument.createRange();
24909         try {
24910             nodeRange.selectNode(node);
24911         } catch (e) {
24912             nodeRange.selectNodeContents(node);
24913         }
24914         
24915         
24916         range.collapse(true);
24917     
24918         nodeRange.collapse(true);
24919      
24920         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24921         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24922          
24923         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24924         
24925         var nodeIsBefore   =  ss == 1;
24926         var nodeIsAfter    = ee == -1;
24927         
24928         if (nodeIsBefore && nodeIsAfter) {
24929             return 0; // outer
24930         }
24931         if (!nodeIsBefore && nodeIsAfter) {
24932             return 1; //right trailed.
24933         }
24934         
24935         if (nodeIsBefore && !nodeIsAfter) {
24936             return 2;  // left trailed.
24937         }
24938         // fully contined.
24939         return 3;
24940     },
24941
24942     // private? - in a new class?
24943     cleanUpPaste :  function()
24944     {
24945         // cleans up the whole document..
24946         Roo.log('cleanuppaste');
24947         
24948         this.cleanUpChildren(this.doc.body);
24949         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24950         if (clean != this.doc.body.innerHTML) {
24951             this.doc.body.innerHTML = clean;
24952         }
24953         
24954     },
24955     
24956     cleanWordChars : function(input) {// change the chars to hex code
24957         var he = Roo.HtmlEditorCore;
24958         
24959         var output = input;
24960         Roo.each(he.swapCodes, function(sw) { 
24961             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24962             
24963             output = output.replace(swapper, sw[1]);
24964         });
24965         
24966         return output;
24967     },
24968     
24969     
24970     cleanUpChildren : function (n)
24971     {
24972         if (!n.childNodes.length) {
24973             return;
24974         }
24975         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24976            this.cleanUpChild(n.childNodes[i]);
24977         }
24978     },
24979     
24980     
24981         
24982     
24983     cleanUpChild : function (node)
24984     {
24985         var ed = this;
24986         //console.log(node);
24987         if (node.nodeName == "#text") {
24988             // clean up silly Windows -- stuff?
24989             return; 
24990         }
24991         if (node.nodeName == "#comment") {
24992             node.parentNode.removeChild(node);
24993             // clean up silly Windows -- stuff?
24994             return; 
24995         }
24996         var lcname = node.tagName.toLowerCase();
24997         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24998         // whitelist of tags..
24999         
25000         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25001             // remove node.
25002             node.parentNode.removeChild(node);
25003             return;
25004             
25005         }
25006         
25007         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25008         
25009         // spans with no attributes - just remove them..
25010         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25011             remove_keep_children = true;
25012         }
25013         
25014         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25015         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25016         
25017         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25018         //    remove_keep_children = true;
25019         //}
25020         
25021         if (remove_keep_children) {
25022             this.cleanUpChildren(node);
25023             // inserts everything just before this node...
25024             while (node.childNodes.length) {
25025                 var cn = node.childNodes[0];
25026                 node.removeChild(cn);
25027                 node.parentNode.insertBefore(cn, node);
25028             }
25029             node.parentNode.removeChild(node);
25030             return;
25031         }
25032         
25033         if (!node.attributes || !node.attributes.length) {
25034             
25035           
25036             
25037             
25038             this.cleanUpChildren(node);
25039             return;
25040         }
25041         
25042         function cleanAttr(n,v)
25043         {
25044             
25045             if (v.match(/^\./) || v.match(/^\//)) {
25046                 return;
25047             }
25048             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25049                 return;
25050             }
25051             if (v.match(/^#/)) {
25052                 return;
25053             }
25054             if (v.match(/^\{/)) { // allow template editing.
25055                 return;
25056             }
25057 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25058             node.removeAttribute(n);
25059             
25060         }
25061         
25062         var cwhite = this.cwhite;
25063         var cblack = this.cblack;
25064             
25065         function cleanStyle(n,v)
25066         {
25067             if (v.match(/expression/)) { //XSS?? should we even bother..
25068                 node.removeAttribute(n);
25069                 return;
25070             }
25071             
25072             var parts = v.split(/;/);
25073             var clean = [];
25074             
25075             Roo.each(parts, function(p) {
25076                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25077                 if (!p.length) {
25078                     return true;
25079                 }
25080                 var l = p.split(':').shift().replace(/\s+/g,'');
25081                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25082                 
25083                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25084 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25085                     //node.removeAttribute(n);
25086                     return true;
25087                 }
25088                 //Roo.log()
25089                 // only allow 'c whitelisted system attributes'
25090                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25091 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25092                     //node.removeAttribute(n);
25093                     return true;
25094                 }
25095                 
25096                 
25097                  
25098                 
25099                 clean.push(p);
25100                 return true;
25101             });
25102             if (clean.length) { 
25103                 node.setAttribute(n, clean.join(';'));
25104             } else {
25105                 node.removeAttribute(n);
25106             }
25107             
25108         }
25109         
25110         
25111         for (var i = node.attributes.length-1; i > -1 ; i--) {
25112             var a = node.attributes[i];
25113             //console.log(a);
25114             
25115             if (a.name.toLowerCase().substr(0,2)=='on')  {
25116                 node.removeAttribute(a.name);
25117                 continue;
25118             }
25119             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25120                 node.removeAttribute(a.name);
25121                 continue;
25122             }
25123             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25124                 cleanAttr(a.name,a.value); // fixme..
25125                 continue;
25126             }
25127             if (a.name == 'style') {
25128                 cleanStyle(a.name,a.value);
25129                 continue;
25130             }
25131             /// clean up MS crap..
25132             // tecnically this should be a list of valid class'es..
25133             
25134             
25135             if (a.name == 'class') {
25136                 if (a.value.match(/^Mso/)) {
25137                     node.removeAttribute('class');
25138                 }
25139                 
25140                 if (a.value.match(/^body$/)) {
25141                     node.removeAttribute('class');
25142                 }
25143                 continue;
25144             }
25145             
25146             // style cleanup!?
25147             // class cleanup?
25148             
25149         }
25150         
25151         
25152         this.cleanUpChildren(node);
25153         
25154         
25155     },
25156     
25157     /**
25158      * Clean up MS wordisms...
25159      */
25160     cleanWord : function(node)
25161     {
25162         if (!node) {
25163             this.cleanWord(this.doc.body);
25164             return;
25165         }
25166         
25167         if(
25168                 node.nodeName == 'SPAN' &&
25169                 !node.hasAttributes() &&
25170                 node.childNodes.length == 1 &&
25171                 node.firstChild.nodeName == "#text"  
25172         ) {
25173             var textNode = node.firstChild;
25174             node.removeChild(textNode);
25175             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25176                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25177             }
25178             node.parentNode.insertBefore(textNode, node);
25179             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25180                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25181             }
25182             node.parentNode.removeChild(node);
25183         }
25184         
25185         if (node.nodeName == "#text") {
25186             // clean up silly Windows -- stuff?
25187             return; 
25188         }
25189         if (node.nodeName == "#comment") {
25190             node.parentNode.removeChild(node);
25191             // clean up silly Windows -- stuff?
25192             return; 
25193         }
25194         
25195         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25196             node.parentNode.removeChild(node);
25197             return;
25198         }
25199         //Roo.log(node.tagName);
25200         // remove - but keep children..
25201         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25202             //Roo.log('-- removed');
25203             while (node.childNodes.length) {
25204                 var cn = node.childNodes[0];
25205                 node.removeChild(cn);
25206                 node.parentNode.insertBefore(cn, node);
25207                 // move node to parent - and clean it..
25208                 this.cleanWord(cn);
25209             }
25210             node.parentNode.removeChild(node);
25211             /// no need to iterate chidlren = it's got none..
25212             //this.iterateChildren(node, this.cleanWord);
25213             return;
25214         }
25215         // clean styles
25216         if (node.className.length) {
25217             
25218             var cn = node.className.split(/\W+/);
25219             var cna = [];
25220             Roo.each(cn, function(cls) {
25221                 if (cls.match(/Mso[a-zA-Z]+/)) {
25222                     return;
25223                 }
25224                 cna.push(cls);
25225             });
25226             node.className = cna.length ? cna.join(' ') : '';
25227             if (!cna.length) {
25228                 node.removeAttribute("class");
25229             }
25230         }
25231         
25232         if (node.hasAttribute("lang")) {
25233             node.removeAttribute("lang");
25234         }
25235         
25236         if (node.hasAttribute("style")) {
25237             
25238             var styles = node.getAttribute("style").split(";");
25239             var nstyle = [];
25240             Roo.each(styles, function(s) {
25241                 if (!s.match(/:/)) {
25242                     return;
25243                 }
25244                 var kv = s.split(":");
25245                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25246                     return;
25247                 }
25248                 // what ever is left... we allow.
25249                 nstyle.push(s);
25250             });
25251             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25252             if (!nstyle.length) {
25253                 node.removeAttribute('style');
25254             }
25255         }
25256         this.iterateChildren(node, this.cleanWord);
25257         
25258         
25259         
25260     },
25261     /**
25262      * iterateChildren of a Node, calling fn each time, using this as the scole..
25263      * @param {DomNode} node node to iterate children of.
25264      * @param {Function} fn method of this class to call on each item.
25265      */
25266     iterateChildren : function(node, fn)
25267     {
25268         if (!node.childNodes.length) {
25269                 return;
25270         }
25271         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25272            fn.call(this, node.childNodes[i])
25273         }
25274     },
25275     
25276     
25277     /**
25278      * cleanTableWidths.
25279      *
25280      * Quite often pasting from word etc.. results in tables with column and widths.
25281      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25282      *
25283      */
25284     cleanTableWidths : function(node)
25285     {
25286          
25287          
25288         if (!node) {
25289             this.cleanTableWidths(this.doc.body);
25290             return;
25291         }
25292         
25293         // ignore list...
25294         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25295             return; 
25296         }
25297         Roo.log(node.tagName);
25298         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25299             this.iterateChildren(node, this.cleanTableWidths);
25300             return;
25301         }
25302         if (node.hasAttribute('width')) {
25303             node.removeAttribute('width');
25304         }
25305         
25306          
25307         if (node.hasAttribute("style")) {
25308             // pretty basic...
25309             
25310             var styles = node.getAttribute("style").split(";");
25311             var nstyle = [];
25312             Roo.each(styles, function(s) {
25313                 if (!s.match(/:/)) {
25314                     return;
25315                 }
25316                 var kv = s.split(":");
25317                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25318                     return;
25319                 }
25320                 // what ever is left... we allow.
25321                 nstyle.push(s);
25322             });
25323             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25324             if (!nstyle.length) {
25325                 node.removeAttribute('style');
25326             }
25327         }
25328         
25329         this.iterateChildren(node, this.cleanTableWidths);
25330         
25331         
25332     },
25333     
25334     
25335     
25336     
25337     domToHTML : function(currentElement, depth, nopadtext) {
25338         
25339         depth = depth || 0;
25340         nopadtext = nopadtext || false;
25341     
25342         if (!currentElement) {
25343             return this.domToHTML(this.doc.body);
25344         }
25345         
25346         //Roo.log(currentElement);
25347         var j;
25348         var allText = false;
25349         var nodeName = currentElement.nodeName;
25350         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25351         
25352         if  (nodeName == '#text') {
25353             
25354             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25355         }
25356         
25357         
25358         var ret = '';
25359         if (nodeName != 'BODY') {
25360              
25361             var i = 0;
25362             // Prints the node tagName, such as <A>, <IMG>, etc
25363             if (tagName) {
25364                 var attr = [];
25365                 for(i = 0; i < currentElement.attributes.length;i++) {
25366                     // quoting?
25367                     var aname = currentElement.attributes.item(i).name;
25368                     if (!currentElement.attributes.item(i).value.length) {
25369                         continue;
25370                     }
25371                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25372                 }
25373                 
25374                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25375             } 
25376             else {
25377                 
25378                 // eack
25379             }
25380         } else {
25381             tagName = false;
25382         }
25383         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25384             return ret;
25385         }
25386         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25387             nopadtext = true;
25388         }
25389         
25390         
25391         // Traverse the tree
25392         i = 0;
25393         var currentElementChild = currentElement.childNodes.item(i);
25394         var allText = true;
25395         var innerHTML  = '';
25396         lastnode = '';
25397         while (currentElementChild) {
25398             // Formatting code (indent the tree so it looks nice on the screen)
25399             var nopad = nopadtext;
25400             if (lastnode == 'SPAN') {
25401                 nopad  = true;
25402             }
25403             // text
25404             if  (currentElementChild.nodeName == '#text') {
25405                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25406                 toadd = nopadtext ? toadd : toadd.trim();
25407                 if (!nopad && toadd.length > 80) {
25408                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25409                 }
25410                 innerHTML  += toadd;
25411                 
25412                 i++;
25413                 currentElementChild = currentElement.childNodes.item(i);
25414                 lastNode = '';
25415                 continue;
25416             }
25417             allText = false;
25418             
25419             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25420                 
25421             // Recursively traverse the tree structure of the child node
25422             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25423             lastnode = currentElementChild.nodeName;
25424             i++;
25425             currentElementChild=currentElement.childNodes.item(i);
25426         }
25427         
25428         ret += innerHTML;
25429         
25430         if (!allText) {
25431                 // The remaining code is mostly for formatting the tree
25432             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25433         }
25434         
25435         
25436         if (tagName) {
25437             ret+= "</"+tagName+">";
25438         }
25439         return ret;
25440         
25441     },
25442         
25443     applyBlacklists : function()
25444     {
25445         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25446         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25447         
25448         this.white = [];
25449         this.black = [];
25450         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25451             if (b.indexOf(tag) > -1) {
25452                 return;
25453             }
25454             this.white.push(tag);
25455             
25456         }, this);
25457         
25458         Roo.each(w, function(tag) {
25459             if (b.indexOf(tag) > -1) {
25460                 return;
25461             }
25462             if (this.white.indexOf(tag) > -1) {
25463                 return;
25464             }
25465             this.white.push(tag);
25466             
25467         }, this);
25468         
25469         
25470         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25471             if (w.indexOf(tag) > -1) {
25472                 return;
25473             }
25474             this.black.push(tag);
25475             
25476         }, this);
25477         
25478         Roo.each(b, function(tag) {
25479             if (w.indexOf(tag) > -1) {
25480                 return;
25481             }
25482             if (this.black.indexOf(tag) > -1) {
25483                 return;
25484             }
25485             this.black.push(tag);
25486             
25487         }, this);
25488         
25489         
25490         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25491         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25492         
25493         this.cwhite = [];
25494         this.cblack = [];
25495         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25496             if (b.indexOf(tag) > -1) {
25497                 return;
25498             }
25499             this.cwhite.push(tag);
25500             
25501         }, this);
25502         
25503         Roo.each(w, function(tag) {
25504             if (b.indexOf(tag) > -1) {
25505                 return;
25506             }
25507             if (this.cwhite.indexOf(tag) > -1) {
25508                 return;
25509             }
25510             this.cwhite.push(tag);
25511             
25512         }, this);
25513         
25514         
25515         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25516             if (w.indexOf(tag) > -1) {
25517                 return;
25518             }
25519             this.cblack.push(tag);
25520             
25521         }, this);
25522         
25523         Roo.each(b, function(tag) {
25524             if (w.indexOf(tag) > -1) {
25525                 return;
25526             }
25527             if (this.cblack.indexOf(tag) > -1) {
25528                 return;
25529             }
25530             this.cblack.push(tag);
25531             
25532         }, this);
25533     },
25534     
25535     setStylesheets : function(stylesheets)
25536     {
25537         if(typeof(stylesheets) == 'string'){
25538             Roo.get(this.iframe.contentDocument.head).createChild({
25539                 tag : 'link',
25540                 rel : 'stylesheet',
25541                 type : 'text/css',
25542                 href : stylesheets
25543             });
25544             
25545             return;
25546         }
25547         var _this = this;
25548      
25549         Roo.each(stylesheets, function(s) {
25550             if(!s.length){
25551                 return;
25552             }
25553             
25554             Roo.get(_this.iframe.contentDocument.head).createChild({
25555                 tag : 'link',
25556                 rel : 'stylesheet',
25557                 type : 'text/css',
25558                 href : s
25559             });
25560         });
25561
25562         
25563     },
25564     
25565     removeStylesheets : function()
25566     {
25567         var _this = this;
25568         
25569         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25570             s.remove();
25571         });
25572     },
25573     
25574     setStyle : function(style)
25575     {
25576         Roo.get(this.iframe.contentDocument.head).createChild({
25577             tag : 'style',
25578             type : 'text/css',
25579             html : style
25580         });
25581
25582         return;
25583     }
25584     
25585     // hide stuff that is not compatible
25586     /**
25587      * @event blur
25588      * @hide
25589      */
25590     /**
25591      * @event change
25592      * @hide
25593      */
25594     /**
25595      * @event focus
25596      * @hide
25597      */
25598     /**
25599      * @event specialkey
25600      * @hide
25601      */
25602     /**
25603      * @cfg {String} fieldClass @hide
25604      */
25605     /**
25606      * @cfg {String} focusClass @hide
25607      */
25608     /**
25609      * @cfg {String} autoCreate @hide
25610      */
25611     /**
25612      * @cfg {String} inputType @hide
25613      */
25614     /**
25615      * @cfg {String} invalidClass @hide
25616      */
25617     /**
25618      * @cfg {String} invalidText @hide
25619      */
25620     /**
25621      * @cfg {String} msgFx @hide
25622      */
25623     /**
25624      * @cfg {String} validateOnBlur @hide
25625      */
25626 });
25627
25628 Roo.HtmlEditorCore.white = [
25629         'area', 'br', 'img', 'input', 'hr', 'wbr',
25630         
25631        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25632        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25633        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25634        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25635        'table',   'ul',         'xmp', 
25636        
25637        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25638       'thead',   'tr', 
25639      
25640       'dir', 'menu', 'ol', 'ul', 'dl',
25641        
25642       'embed',  'object'
25643 ];
25644
25645
25646 Roo.HtmlEditorCore.black = [
25647     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25648         'applet', // 
25649         'base',   'basefont', 'bgsound', 'blink',  'body', 
25650         'frame',  'frameset', 'head',    'html',   'ilayer', 
25651         'iframe', 'layer',  'link',     'meta',    'object',   
25652         'script', 'style' ,'title',  'xml' // clean later..
25653 ];
25654 Roo.HtmlEditorCore.clean = [
25655     'script', 'style', 'title', 'xml'
25656 ];
25657 Roo.HtmlEditorCore.remove = [
25658     'font'
25659 ];
25660 // attributes..
25661
25662 Roo.HtmlEditorCore.ablack = [
25663     'on'
25664 ];
25665     
25666 Roo.HtmlEditorCore.aclean = [ 
25667     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25668 ];
25669
25670 // protocols..
25671 Roo.HtmlEditorCore.pwhite= [
25672         'http',  'https',  'mailto'
25673 ];
25674
25675 // white listed style attributes.
25676 Roo.HtmlEditorCore.cwhite= [
25677       //  'text-align', /// default is to allow most things..
25678       
25679          
25680 //        'font-size'//??
25681 ];
25682
25683 // black listed style attributes.
25684 Roo.HtmlEditorCore.cblack= [
25685       //  'font-size' -- this can be set by the project 
25686 ];
25687
25688
25689 Roo.HtmlEditorCore.swapCodes   =[ 
25690     [    8211, "--" ], 
25691     [    8212, "--" ], 
25692     [    8216,  "'" ],  
25693     [    8217, "'" ],  
25694     [    8220, '"' ],  
25695     [    8221, '"' ],  
25696     [    8226, "*" ],  
25697     [    8230, "..." ]
25698 ]; 
25699
25700     /*
25701  * - LGPL
25702  *
25703  * HtmlEditor
25704  * 
25705  */
25706
25707 /**
25708  * @class Roo.bootstrap.HtmlEditor
25709  * @extends Roo.bootstrap.TextArea
25710  * Bootstrap HtmlEditor class
25711
25712  * @constructor
25713  * Create a new HtmlEditor
25714  * @param {Object} config The config object
25715  */
25716
25717 Roo.bootstrap.HtmlEditor = function(config){
25718     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25719     if (!this.toolbars) {
25720         this.toolbars = [];
25721     }
25722     
25723     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25724     this.addEvents({
25725             /**
25726              * @event initialize
25727              * Fires when the editor is fully initialized (including the iframe)
25728              * @param {HtmlEditor} this
25729              */
25730             initialize: true,
25731             /**
25732              * @event activate
25733              * Fires when the editor is first receives the focus. Any insertion must wait
25734              * until after this event.
25735              * @param {HtmlEditor} this
25736              */
25737             activate: true,
25738              /**
25739              * @event beforesync
25740              * Fires before the textarea is updated with content from the editor iframe. Return false
25741              * to cancel the sync.
25742              * @param {HtmlEditor} this
25743              * @param {String} html
25744              */
25745             beforesync: true,
25746              /**
25747              * @event beforepush
25748              * Fires before the iframe editor is updated with content from the textarea. Return false
25749              * to cancel the push.
25750              * @param {HtmlEditor} this
25751              * @param {String} html
25752              */
25753             beforepush: true,
25754              /**
25755              * @event sync
25756              * Fires when the textarea is updated with content from the editor iframe.
25757              * @param {HtmlEditor} this
25758              * @param {String} html
25759              */
25760             sync: true,
25761              /**
25762              * @event push
25763              * Fires when the iframe editor is updated with content from the textarea.
25764              * @param {HtmlEditor} this
25765              * @param {String} html
25766              */
25767             push: true,
25768              /**
25769              * @event editmodechange
25770              * Fires when the editor switches edit modes
25771              * @param {HtmlEditor} this
25772              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25773              */
25774             editmodechange: true,
25775             /**
25776              * @event editorevent
25777              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25778              * @param {HtmlEditor} this
25779              */
25780             editorevent: true,
25781             /**
25782              * @event firstfocus
25783              * Fires when on first focus - needed by toolbars..
25784              * @param {HtmlEditor} this
25785              */
25786             firstfocus: true,
25787             /**
25788              * @event autosave
25789              * Auto save the htmlEditor value as a file into Events
25790              * @param {HtmlEditor} this
25791              */
25792             autosave: true,
25793             /**
25794              * @event savedpreview
25795              * preview the saved version of htmlEditor
25796              * @param {HtmlEditor} this
25797              */
25798             savedpreview: true
25799         });
25800 };
25801
25802
25803 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25804     
25805     
25806       /**
25807      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25808      */
25809     toolbars : false,
25810     
25811      /**
25812     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25813     */
25814     btns : [],
25815    
25816      /**
25817      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25818      *                        Roo.resizable.
25819      */
25820     resizable : false,
25821      /**
25822      * @cfg {Number} height (in pixels)
25823      */   
25824     height: 300,
25825    /**
25826      * @cfg {Number} width (in pixels)
25827      */   
25828     width: false,
25829     
25830     /**
25831      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25832      * 
25833      */
25834     stylesheets: false,
25835     
25836     // id of frame..
25837     frameId: false,
25838     
25839     // private properties
25840     validationEvent : false,
25841     deferHeight: true,
25842     initialized : false,
25843     activated : false,
25844     
25845     onFocus : Roo.emptyFn,
25846     iframePad:3,
25847     hideMode:'offsets',
25848     
25849     tbContainer : false,
25850     
25851     bodyCls : '',
25852     
25853     toolbarContainer :function() {
25854         return this.wrap.select('.x-html-editor-tb',true).first();
25855     },
25856
25857     /**
25858      * Protected method that will not generally be called directly. It
25859      * is called when the editor creates its toolbar. Override this method if you need to
25860      * add custom toolbar buttons.
25861      * @param {HtmlEditor} editor
25862      */
25863     createToolbar : function(){
25864         Roo.log('renewing');
25865         Roo.log("create toolbars");
25866         
25867         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25868         this.toolbars[0].render(this.toolbarContainer());
25869         
25870         return;
25871         
25872 //        if (!editor.toolbars || !editor.toolbars.length) {
25873 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25874 //        }
25875 //        
25876 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25877 //            editor.toolbars[i] = Roo.factory(
25878 //                    typeof(editor.toolbars[i]) == 'string' ?
25879 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25880 //                Roo.bootstrap.HtmlEditor);
25881 //            editor.toolbars[i].init(editor);
25882 //        }
25883     },
25884
25885      
25886     // private
25887     onRender : function(ct, position)
25888     {
25889        // Roo.log("Call onRender: " + this.xtype);
25890         var _t = this;
25891         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25892       
25893         this.wrap = this.inputEl().wrap({
25894             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25895         });
25896         
25897         this.editorcore.onRender(ct, position);
25898          
25899         if (this.resizable) {
25900             this.resizeEl = new Roo.Resizable(this.wrap, {
25901                 pinned : true,
25902                 wrap: true,
25903                 dynamic : true,
25904                 minHeight : this.height,
25905                 height: this.height,
25906                 handles : this.resizable,
25907                 width: this.width,
25908                 listeners : {
25909                     resize : function(r, w, h) {
25910                         _t.onResize(w,h); // -something
25911                     }
25912                 }
25913             });
25914             
25915         }
25916         this.createToolbar(this);
25917        
25918         
25919         if(!this.width && this.resizable){
25920             this.setSize(this.wrap.getSize());
25921         }
25922         if (this.resizeEl) {
25923             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25924             // should trigger onReize..
25925         }
25926         
25927     },
25928
25929     // private
25930     onResize : function(w, h)
25931     {
25932         Roo.log('resize: ' +w + ',' + h );
25933         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25934         var ew = false;
25935         var eh = false;
25936         
25937         if(this.inputEl() ){
25938             if(typeof w == 'number'){
25939                 var aw = w - this.wrap.getFrameWidth('lr');
25940                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25941                 ew = aw;
25942             }
25943             if(typeof h == 'number'){
25944                  var tbh = -11;  // fixme it needs to tool bar size!
25945                 for (var i =0; i < this.toolbars.length;i++) {
25946                     // fixme - ask toolbars for heights?
25947                     tbh += this.toolbars[i].el.getHeight();
25948                     //if (this.toolbars[i].footer) {
25949                     //    tbh += this.toolbars[i].footer.el.getHeight();
25950                     //}
25951                 }
25952               
25953                 
25954                 
25955                 
25956                 
25957                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25958                 ah -= 5; // knock a few pixes off for look..
25959                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25960                 var eh = ah;
25961             }
25962         }
25963         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25964         this.editorcore.onResize(ew,eh);
25965         
25966     },
25967
25968     /**
25969      * Toggles the editor between standard and source edit mode.
25970      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25971      */
25972     toggleSourceEdit : function(sourceEditMode)
25973     {
25974         this.editorcore.toggleSourceEdit(sourceEditMode);
25975         
25976         if(this.editorcore.sourceEditMode){
25977             Roo.log('editor - showing textarea');
25978             
25979 //            Roo.log('in');
25980 //            Roo.log(this.syncValue());
25981             this.syncValue();
25982             this.inputEl().removeClass(['hide', 'x-hidden']);
25983             this.inputEl().dom.removeAttribute('tabIndex');
25984             this.inputEl().focus();
25985         }else{
25986             Roo.log('editor - hiding textarea');
25987 //            Roo.log('out')
25988 //            Roo.log(this.pushValue()); 
25989             this.pushValue();
25990             
25991             this.inputEl().addClass(['hide', 'x-hidden']);
25992             this.inputEl().dom.setAttribute('tabIndex', -1);
25993             //this.deferFocus();
25994         }
25995          
25996         if(this.resizable){
25997             this.setSize(this.wrap.getSize());
25998         }
25999         
26000         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26001     },
26002  
26003     // private (for BoxComponent)
26004     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26005
26006     // private (for BoxComponent)
26007     getResizeEl : function(){
26008         return this.wrap;
26009     },
26010
26011     // private (for BoxComponent)
26012     getPositionEl : function(){
26013         return this.wrap;
26014     },
26015
26016     // private
26017     initEvents : function(){
26018         this.originalValue = this.getValue();
26019     },
26020
26021 //    /**
26022 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26023 //     * @method
26024 //     */
26025 //    markInvalid : Roo.emptyFn,
26026 //    /**
26027 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26028 //     * @method
26029 //     */
26030 //    clearInvalid : Roo.emptyFn,
26031
26032     setValue : function(v){
26033         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26034         this.editorcore.pushValue();
26035     },
26036
26037      
26038     // private
26039     deferFocus : function(){
26040         this.focus.defer(10, this);
26041     },
26042
26043     // doc'ed in Field
26044     focus : function(){
26045         this.editorcore.focus();
26046         
26047     },
26048       
26049
26050     // private
26051     onDestroy : function(){
26052         
26053         
26054         
26055         if(this.rendered){
26056             
26057             for (var i =0; i < this.toolbars.length;i++) {
26058                 // fixme - ask toolbars for heights?
26059                 this.toolbars[i].onDestroy();
26060             }
26061             
26062             this.wrap.dom.innerHTML = '';
26063             this.wrap.remove();
26064         }
26065     },
26066
26067     // private
26068     onFirstFocus : function(){
26069         //Roo.log("onFirstFocus");
26070         this.editorcore.onFirstFocus();
26071          for (var i =0; i < this.toolbars.length;i++) {
26072             this.toolbars[i].onFirstFocus();
26073         }
26074         
26075     },
26076     
26077     // private
26078     syncValue : function()
26079     {   
26080         this.editorcore.syncValue();
26081     },
26082     
26083     pushValue : function()
26084     {   
26085         this.editorcore.pushValue();
26086     }
26087      
26088     
26089     // hide stuff that is not compatible
26090     /**
26091      * @event blur
26092      * @hide
26093      */
26094     /**
26095      * @event change
26096      * @hide
26097      */
26098     /**
26099      * @event focus
26100      * @hide
26101      */
26102     /**
26103      * @event specialkey
26104      * @hide
26105      */
26106     /**
26107      * @cfg {String} fieldClass @hide
26108      */
26109     /**
26110      * @cfg {String} focusClass @hide
26111      */
26112     /**
26113      * @cfg {String} autoCreate @hide
26114      */
26115     /**
26116      * @cfg {String} inputType @hide
26117      */
26118      
26119     /**
26120      * @cfg {String} invalidText @hide
26121      */
26122     /**
26123      * @cfg {String} msgFx @hide
26124      */
26125     /**
26126      * @cfg {String} validateOnBlur @hide
26127      */
26128 });
26129  
26130     
26131    
26132    
26133    
26134       
26135 Roo.namespace('Roo.bootstrap.htmleditor');
26136 /**
26137  * @class Roo.bootstrap.HtmlEditorToolbar1
26138  * Basic Toolbar
26139  * 
26140  * @example
26141  * Usage:
26142  *
26143  new Roo.bootstrap.HtmlEditor({
26144     ....
26145     toolbars : [
26146         new Roo.bootstrap.HtmlEditorToolbar1({
26147             disable : { fonts: 1 , format: 1, ..., ... , ...],
26148             btns : [ .... ]
26149         })
26150     }
26151      
26152  * 
26153  * @cfg {Object} disable List of elements to disable..
26154  * @cfg {Array} btns List of additional buttons.
26155  * 
26156  * 
26157  * NEEDS Extra CSS? 
26158  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26159  */
26160  
26161 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26162 {
26163     
26164     Roo.apply(this, config);
26165     
26166     // default disabled, based on 'good practice'..
26167     this.disable = this.disable || {};
26168     Roo.applyIf(this.disable, {
26169         fontSize : true,
26170         colors : true,
26171         specialElements : true
26172     });
26173     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26174     
26175     this.editor = config.editor;
26176     this.editorcore = config.editor.editorcore;
26177     
26178     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26179     
26180     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26181     // dont call parent... till later.
26182 }
26183 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26184      
26185     bar : true,
26186     
26187     editor : false,
26188     editorcore : false,
26189     
26190     
26191     formats : [
26192         "p" ,  
26193         "h1","h2","h3","h4","h5","h6", 
26194         "pre", "code", 
26195         "abbr", "acronym", "address", "cite", "samp", "var",
26196         'div','span'
26197     ],
26198     
26199     onRender : function(ct, position)
26200     {
26201        // Roo.log("Call onRender: " + this.xtype);
26202         
26203        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26204        Roo.log(this.el);
26205        this.el.dom.style.marginBottom = '0';
26206        var _this = this;
26207        var editorcore = this.editorcore;
26208        var editor= this.editor;
26209        
26210        var children = [];
26211        var btn = function(id,cmd , toggle, handler, html){
26212        
26213             var  event = toggle ? 'toggle' : 'click';
26214        
26215             var a = {
26216                 size : 'sm',
26217                 xtype: 'Button',
26218                 xns: Roo.bootstrap,
26219                 //glyphicon : id,
26220                 fa: id,
26221                 cmd : id || cmd,
26222                 enableToggle:toggle !== false,
26223                 html : html || '',
26224                 pressed : toggle ? false : null,
26225                 listeners : {}
26226             };
26227             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26228                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26229             };
26230             children.push(a);
26231             return a;
26232        }
26233        
26234     //    var cb_box = function...
26235         
26236         var style = {
26237                 xtype: 'Button',
26238                 size : 'sm',
26239                 xns: Roo.bootstrap,
26240                 fa : 'font',
26241                 //html : 'submit'
26242                 menu : {
26243                     xtype: 'Menu',
26244                     xns: Roo.bootstrap,
26245                     items:  []
26246                 }
26247         };
26248         Roo.each(this.formats, function(f) {
26249             style.menu.items.push({
26250                 xtype :'MenuItem',
26251                 xns: Roo.bootstrap,
26252                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26253                 tagname : f,
26254                 listeners : {
26255                     click : function()
26256                     {
26257                         editorcore.insertTag(this.tagname);
26258                         editor.focus();
26259                     }
26260                 }
26261                 
26262             });
26263         });
26264         children.push(style);   
26265         
26266         btn('bold',false,true);
26267         btn('italic',false,true);
26268         btn('align-left', 'justifyleft',true);
26269         btn('align-center', 'justifycenter',true);
26270         btn('align-right' , 'justifyright',true);
26271         btn('link', false, false, function(btn) {
26272             //Roo.log("create link?");
26273             var url = prompt(this.createLinkText, this.defaultLinkValue);
26274             if(url && url != 'http:/'+'/'){
26275                 this.editorcore.relayCmd('createlink', url);
26276             }
26277         }),
26278         btn('list','insertunorderedlist',true);
26279         btn('pencil', false,true, function(btn){
26280                 Roo.log(this);
26281                 this.toggleSourceEdit(btn.pressed);
26282         });
26283         
26284         if (this.editor.btns.length > 0) {
26285             for (var i = 0; i<this.editor.btns.length; i++) {
26286                 children.push(this.editor.btns[i]);
26287             }
26288         }
26289         
26290         /*
26291         var cog = {
26292                 xtype: 'Button',
26293                 size : 'sm',
26294                 xns: Roo.bootstrap,
26295                 glyphicon : 'cog',
26296                 //html : 'submit'
26297                 menu : {
26298                     xtype: 'Menu',
26299                     xns: Roo.bootstrap,
26300                     items:  []
26301                 }
26302         };
26303         
26304         cog.menu.items.push({
26305             xtype :'MenuItem',
26306             xns: Roo.bootstrap,
26307             html : Clean styles,
26308             tagname : f,
26309             listeners : {
26310                 click : function()
26311                 {
26312                     editorcore.insertTag(this.tagname);
26313                     editor.focus();
26314                 }
26315             }
26316             
26317         });
26318        */
26319         
26320          
26321        this.xtype = 'NavSimplebar';
26322         
26323         for(var i=0;i< children.length;i++) {
26324             
26325             this.buttons.add(this.addxtypeChild(children[i]));
26326             
26327         }
26328         
26329         editor.on('editorevent', this.updateToolbar, this);
26330     },
26331     onBtnClick : function(id)
26332     {
26333        this.editorcore.relayCmd(id);
26334        this.editorcore.focus();
26335     },
26336     
26337     /**
26338      * Protected method that will not generally be called directly. It triggers
26339      * a toolbar update by reading the markup state of the current selection in the editor.
26340      */
26341     updateToolbar: function(){
26342
26343         if(!this.editorcore.activated){
26344             this.editor.onFirstFocus(); // is this neeed?
26345             return;
26346         }
26347
26348         var btns = this.buttons; 
26349         var doc = this.editorcore.doc;
26350         btns.get('bold').setActive(doc.queryCommandState('bold'));
26351         btns.get('italic').setActive(doc.queryCommandState('italic'));
26352         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26353         
26354         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26355         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26356         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26357         
26358         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26359         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26360          /*
26361         
26362         var ans = this.editorcore.getAllAncestors();
26363         if (this.formatCombo) {
26364             
26365             
26366             var store = this.formatCombo.store;
26367             this.formatCombo.setValue("");
26368             for (var i =0; i < ans.length;i++) {
26369                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26370                     // select it..
26371                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26372                     break;
26373                 }
26374             }
26375         }
26376         
26377         
26378         
26379         // hides menus... - so this cant be on a menu...
26380         Roo.bootstrap.MenuMgr.hideAll();
26381         */
26382         Roo.bootstrap.MenuMgr.hideAll();
26383         //this.editorsyncValue();
26384     },
26385     onFirstFocus: function() {
26386         this.buttons.each(function(item){
26387            item.enable();
26388         });
26389     },
26390     toggleSourceEdit : function(sourceEditMode){
26391         
26392           
26393         if(sourceEditMode){
26394             Roo.log("disabling buttons");
26395            this.buttons.each( function(item){
26396                 if(item.cmd != 'pencil'){
26397                     item.disable();
26398                 }
26399             });
26400           
26401         }else{
26402             Roo.log("enabling buttons");
26403             if(this.editorcore.initialized){
26404                 this.buttons.each( function(item){
26405                     item.enable();
26406                 });
26407             }
26408             
26409         }
26410         Roo.log("calling toggole on editor");
26411         // tell the editor that it's been pressed..
26412         this.editor.toggleSourceEdit(sourceEditMode);
26413        
26414     }
26415 });
26416
26417
26418
26419
26420  
26421 /*
26422  * - LGPL
26423  */
26424
26425 /**
26426  * @class Roo.bootstrap.Markdown
26427  * @extends Roo.bootstrap.TextArea
26428  * Bootstrap Showdown editable area
26429  * @cfg {string} content
26430  * 
26431  * @constructor
26432  * Create a new Showdown
26433  */
26434
26435 Roo.bootstrap.Markdown = function(config){
26436     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26437    
26438 };
26439
26440 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26441     
26442     editing :false,
26443     
26444     initEvents : function()
26445     {
26446         
26447         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26448         this.markdownEl = this.el.createChild({
26449             cls : 'roo-markdown-area'
26450         });
26451         this.inputEl().addClass('d-none');
26452         if (this.getValue() == '') {
26453             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26454             
26455         } else {
26456             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26457         }
26458         this.markdownEl.on('click', this.toggleTextEdit, this);
26459         this.on('blur', this.toggleTextEdit, this);
26460         this.on('specialkey', this.resizeTextArea, this);
26461     },
26462     
26463     toggleTextEdit : function()
26464     {
26465         var sh = this.markdownEl.getHeight();
26466         this.inputEl().addClass('d-none');
26467         this.markdownEl.addClass('d-none');
26468         if (!this.editing) {
26469             // show editor?
26470             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26471             this.inputEl().removeClass('d-none');
26472             this.inputEl().focus();
26473             this.editing = true;
26474             return;
26475         }
26476         // show showdown...
26477         this.updateMarkdown();
26478         this.markdownEl.removeClass('d-none');
26479         this.editing = false;
26480         return;
26481     },
26482     updateMarkdown : function()
26483     {
26484         if (this.getValue() == '') {
26485             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26486             return;
26487         }
26488  
26489         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26490     },
26491     
26492     resizeTextArea: function () {
26493         
26494         var sh = 100;
26495         Roo.log([sh, this.getValue().split("\n").length * 30]);
26496         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26497     },
26498     setValue : function(val)
26499     {
26500         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26501         if (!this.editing) {
26502             this.updateMarkdown();
26503         }
26504         
26505     },
26506     focus : function()
26507     {
26508         if (!this.editing) {
26509             this.toggleTextEdit();
26510         }
26511         
26512     }
26513
26514
26515 });
26516 /**
26517  * @class Roo.bootstrap.Table.AbstractSelectionModel
26518  * @extends Roo.util.Observable
26519  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26520  * implemented by descendant classes.  This class should not be directly instantiated.
26521  * @constructor
26522  */
26523 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26524     this.locked = false;
26525     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26526 };
26527
26528
26529 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26530     /** @ignore Called by the grid automatically. Do not call directly. */
26531     init : function(grid){
26532         this.grid = grid;
26533         this.initEvents();
26534     },
26535
26536     /**
26537      * Locks the selections.
26538      */
26539     lock : function(){
26540         this.locked = true;
26541     },
26542
26543     /**
26544      * Unlocks the selections.
26545      */
26546     unlock : function(){
26547         this.locked = false;
26548     },
26549
26550     /**
26551      * Returns true if the selections are locked.
26552      * @return {Boolean}
26553      */
26554     isLocked : function(){
26555         return this.locked;
26556     },
26557     
26558     
26559     initEvents : function ()
26560     {
26561         
26562     }
26563 });
26564 /**
26565  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26566  * @class Roo.bootstrap.Table.RowSelectionModel
26567  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26568  * It supports multiple selections and keyboard selection/navigation. 
26569  * @constructor
26570  * @param {Object} config
26571  */
26572
26573 Roo.bootstrap.Table.RowSelectionModel = function(config){
26574     Roo.apply(this, config);
26575     this.selections = new Roo.util.MixedCollection(false, function(o){
26576         return o.id;
26577     });
26578
26579     this.last = false;
26580     this.lastActive = false;
26581
26582     this.addEvents({
26583         /**
26584              * @event selectionchange
26585              * Fires when the selection changes
26586              * @param {SelectionModel} this
26587              */
26588             "selectionchange" : true,
26589         /**
26590              * @event afterselectionchange
26591              * Fires after the selection changes (eg. by key press or clicking)
26592              * @param {SelectionModel} this
26593              */
26594             "afterselectionchange" : true,
26595         /**
26596              * @event beforerowselect
26597              * Fires when a row is selected being selected, return false to cancel.
26598              * @param {SelectionModel} this
26599              * @param {Number} rowIndex The selected index
26600              * @param {Boolean} keepExisting False if other selections will be cleared
26601              */
26602             "beforerowselect" : true,
26603         /**
26604              * @event rowselect
26605              * Fires when a row is selected.
26606              * @param {SelectionModel} this
26607              * @param {Number} rowIndex The selected index
26608              * @param {Roo.data.Record} r The record
26609              */
26610             "rowselect" : true,
26611         /**
26612              * @event rowdeselect
26613              * Fires when a row is deselected.
26614              * @param {SelectionModel} this
26615              * @param {Number} rowIndex The selected index
26616              */
26617         "rowdeselect" : true
26618     });
26619     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26620     this.locked = false;
26621  };
26622
26623 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26624     /**
26625      * @cfg {Boolean} singleSelect
26626      * True to allow selection of only one row at a time (defaults to false)
26627      */
26628     singleSelect : false,
26629
26630     // private
26631     initEvents : function()
26632     {
26633
26634         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26635         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26636         //}else{ // allow click to work like normal
26637          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26638         //}
26639         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26640         this.grid.on("rowclick", this.handleMouseDown, this);
26641         
26642         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26643             "up" : function(e){
26644                 if(!e.shiftKey){
26645                     this.selectPrevious(e.shiftKey);
26646                 }else if(this.last !== false && this.lastActive !== false){
26647                     var last = this.last;
26648                     this.selectRange(this.last,  this.lastActive-1);
26649                     this.grid.getView().focusRow(this.lastActive);
26650                     if(last !== false){
26651                         this.last = last;
26652                     }
26653                 }else{
26654                     this.selectFirstRow();
26655                 }
26656                 this.fireEvent("afterselectionchange", this);
26657             },
26658             "down" : function(e){
26659                 if(!e.shiftKey){
26660                     this.selectNext(e.shiftKey);
26661                 }else if(this.last !== false && this.lastActive !== false){
26662                     var last = this.last;
26663                     this.selectRange(this.last,  this.lastActive+1);
26664                     this.grid.getView().focusRow(this.lastActive);
26665                     if(last !== false){
26666                         this.last = last;
26667                     }
26668                 }else{
26669                     this.selectFirstRow();
26670                 }
26671                 this.fireEvent("afterselectionchange", this);
26672             },
26673             scope: this
26674         });
26675         this.grid.store.on('load', function(){
26676             this.selections.clear();
26677         },this);
26678         /*
26679         var view = this.grid.view;
26680         view.on("refresh", this.onRefresh, this);
26681         view.on("rowupdated", this.onRowUpdated, this);
26682         view.on("rowremoved", this.onRemove, this);
26683         */
26684     },
26685
26686     // private
26687     onRefresh : function()
26688     {
26689         var ds = this.grid.store, i, v = this.grid.view;
26690         var s = this.selections;
26691         s.each(function(r){
26692             if((i = ds.indexOfId(r.id)) != -1){
26693                 v.onRowSelect(i);
26694             }else{
26695                 s.remove(r);
26696             }
26697         });
26698     },
26699
26700     // private
26701     onRemove : function(v, index, r){
26702         this.selections.remove(r);
26703     },
26704
26705     // private
26706     onRowUpdated : function(v, index, r){
26707         if(this.isSelected(r)){
26708             v.onRowSelect(index);
26709         }
26710     },
26711
26712     /**
26713      * Select records.
26714      * @param {Array} records The records to select
26715      * @param {Boolean} keepExisting (optional) True to keep existing selections
26716      */
26717     selectRecords : function(records, keepExisting)
26718     {
26719         if(!keepExisting){
26720             this.clearSelections();
26721         }
26722             var ds = this.grid.store;
26723         for(var i = 0, len = records.length; i < len; i++){
26724             this.selectRow(ds.indexOf(records[i]), true);
26725         }
26726     },
26727
26728     /**
26729      * Gets the number of selected rows.
26730      * @return {Number}
26731      */
26732     getCount : function(){
26733         return this.selections.length;
26734     },
26735
26736     /**
26737      * Selects the first row in the grid.
26738      */
26739     selectFirstRow : function(){
26740         this.selectRow(0);
26741     },
26742
26743     /**
26744      * Select the last row.
26745      * @param {Boolean} keepExisting (optional) True to keep existing selections
26746      */
26747     selectLastRow : function(keepExisting){
26748         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26749         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26750     },
26751
26752     /**
26753      * Selects the row immediately following the last selected row.
26754      * @param {Boolean} keepExisting (optional) True to keep existing selections
26755      */
26756     selectNext : function(keepExisting)
26757     {
26758             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26759             this.selectRow(this.last+1, keepExisting);
26760             this.grid.getView().focusRow(this.last);
26761         }
26762     },
26763
26764     /**
26765      * Selects the row that precedes the last selected row.
26766      * @param {Boolean} keepExisting (optional) True to keep existing selections
26767      */
26768     selectPrevious : function(keepExisting){
26769         if(this.last){
26770             this.selectRow(this.last-1, keepExisting);
26771             this.grid.getView().focusRow(this.last);
26772         }
26773     },
26774
26775     /**
26776      * Returns the selected records
26777      * @return {Array} Array of selected records
26778      */
26779     getSelections : function(){
26780         return [].concat(this.selections.items);
26781     },
26782
26783     /**
26784      * Returns the first selected record.
26785      * @return {Record}
26786      */
26787     getSelected : function(){
26788         return this.selections.itemAt(0);
26789     },
26790
26791
26792     /**
26793      * Clears all selections.
26794      */
26795     clearSelections : function(fast)
26796     {
26797         if(this.locked) {
26798             return;
26799         }
26800         if(fast !== true){
26801                 var ds = this.grid.store;
26802             var s = this.selections;
26803             s.each(function(r){
26804                 this.deselectRow(ds.indexOfId(r.id));
26805             }, this);
26806             s.clear();
26807         }else{
26808             this.selections.clear();
26809         }
26810         this.last = false;
26811     },
26812
26813
26814     /**
26815      * Selects all rows.
26816      */
26817     selectAll : function(){
26818         if(this.locked) {
26819             return;
26820         }
26821         this.selections.clear();
26822         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26823             this.selectRow(i, true);
26824         }
26825     },
26826
26827     /**
26828      * Returns True if there is a selection.
26829      * @return {Boolean}
26830      */
26831     hasSelection : function(){
26832         return this.selections.length > 0;
26833     },
26834
26835     /**
26836      * Returns True if the specified row is selected.
26837      * @param {Number/Record} record The record or index of the record to check
26838      * @return {Boolean}
26839      */
26840     isSelected : function(index){
26841             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26842         return (r && this.selections.key(r.id) ? true : false);
26843     },
26844
26845     /**
26846      * Returns True if the specified record id is selected.
26847      * @param {String} id The id of record to check
26848      * @return {Boolean}
26849      */
26850     isIdSelected : function(id){
26851         return (this.selections.key(id) ? true : false);
26852     },
26853
26854
26855     // private
26856     handleMouseDBClick : function(e, t){
26857         
26858     },
26859     // private
26860     handleMouseDown : function(e, t)
26861     {
26862             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26863         if(this.isLocked() || rowIndex < 0 ){
26864             return;
26865         };
26866         if(e.shiftKey && this.last !== false){
26867             var last = this.last;
26868             this.selectRange(last, rowIndex, e.ctrlKey);
26869             this.last = last; // reset the last
26870             t.focus();
26871     
26872         }else{
26873             var isSelected = this.isSelected(rowIndex);
26874             //Roo.log("select row:" + rowIndex);
26875             if(isSelected){
26876                 this.deselectRow(rowIndex);
26877             } else {
26878                         this.selectRow(rowIndex, true);
26879             }
26880     
26881             /*
26882                 if(e.button !== 0 && isSelected){
26883                 alert('rowIndex 2: ' + rowIndex);
26884                     view.focusRow(rowIndex);
26885                 }else if(e.ctrlKey && isSelected){
26886                     this.deselectRow(rowIndex);
26887                 }else if(!isSelected){
26888                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26889                     view.focusRow(rowIndex);
26890                 }
26891             */
26892         }
26893         this.fireEvent("afterselectionchange", this);
26894     },
26895     // private
26896     handleDragableRowClick :  function(grid, rowIndex, e) 
26897     {
26898         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26899             this.selectRow(rowIndex, false);
26900             grid.view.focusRow(rowIndex);
26901              this.fireEvent("afterselectionchange", this);
26902         }
26903     },
26904     
26905     /**
26906      * Selects multiple rows.
26907      * @param {Array} rows Array of the indexes of the row to select
26908      * @param {Boolean} keepExisting (optional) True to keep existing selections
26909      */
26910     selectRows : function(rows, keepExisting){
26911         if(!keepExisting){
26912             this.clearSelections();
26913         }
26914         for(var i = 0, len = rows.length; i < len; i++){
26915             this.selectRow(rows[i], true);
26916         }
26917     },
26918
26919     /**
26920      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26921      * @param {Number} startRow The index of the first row in the range
26922      * @param {Number} endRow The index of the last row in the range
26923      * @param {Boolean} keepExisting (optional) True to retain existing selections
26924      */
26925     selectRange : function(startRow, endRow, keepExisting){
26926         if(this.locked) {
26927             return;
26928         }
26929         if(!keepExisting){
26930             this.clearSelections();
26931         }
26932         if(startRow <= endRow){
26933             for(var i = startRow; i <= endRow; i++){
26934                 this.selectRow(i, true);
26935             }
26936         }else{
26937             for(var i = startRow; i >= endRow; i--){
26938                 this.selectRow(i, true);
26939             }
26940         }
26941     },
26942
26943     /**
26944      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26945      * @param {Number} startRow The index of the first row in the range
26946      * @param {Number} endRow The index of the last row in the range
26947      */
26948     deselectRange : function(startRow, endRow, preventViewNotify){
26949         if(this.locked) {
26950             return;
26951         }
26952         for(var i = startRow; i <= endRow; i++){
26953             this.deselectRow(i, preventViewNotify);
26954         }
26955     },
26956
26957     /**
26958      * Selects a row.
26959      * @param {Number} row The index of the row to select
26960      * @param {Boolean} keepExisting (optional) True to keep existing selections
26961      */
26962     selectRow : function(index, keepExisting, preventViewNotify)
26963     {
26964             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26965             return;
26966         }
26967         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26968             if(!keepExisting || this.singleSelect){
26969                 this.clearSelections();
26970             }
26971             
26972             var r = this.grid.store.getAt(index);
26973             //console.log('selectRow - record id :' + r.id);
26974             
26975             this.selections.add(r);
26976             this.last = this.lastActive = index;
26977             if(!preventViewNotify){
26978                 var proxy = new Roo.Element(
26979                                 this.grid.getRowDom(index)
26980                 );
26981                 proxy.addClass('bg-info info');
26982             }
26983             this.fireEvent("rowselect", this, index, r);
26984             this.fireEvent("selectionchange", this);
26985         }
26986     },
26987
26988     /**
26989      * Deselects a row.
26990      * @param {Number} row The index of the row to deselect
26991      */
26992     deselectRow : function(index, preventViewNotify)
26993     {
26994         if(this.locked) {
26995             return;
26996         }
26997         if(this.last == index){
26998             this.last = false;
26999         }
27000         if(this.lastActive == index){
27001             this.lastActive = false;
27002         }
27003         
27004         var r = this.grid.store.getAt(index);
27005         if (!r) {
27006             return;
27007         }
27008         
27009         this.selections.remove(r);
27010         //.console.log('deselectRow - record id :' + r.id);
27011         if(!preventViewNotify){
27012         
27013             var proxy = new Roo.Element(
27014                 this.grid.getRowDom(index)
27015             );
27016             proxy.removeClass('bg-info info');
27017         }
27018         this.fireEvent("rowdeselect", this, index);
27019         this.fireEvent("selectionchange", this);
27020     },
27021
27022     // private
27023     restoreLast : function(){
27024         if(this._last){
27025             this.last = this._last;
27026         }
27027     },
27028
27029     // private
27030     acceptsNav : function(row, col, cm){
27031         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27032     },
27033
27034     // private
27035     onEditorKey : function(field, e){
27036         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27037         if(k == e.TAB){
27038             e.stopEvent();
27039             ed.completeEdit();
27040             if(e.shiftKey){
27041                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27042             }else{
27043                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27044             }
27045         }else if(k == e.ENTER && !e.ctrlKey){
27046             e.stopEvent();
27047             ed.completeEdit();
27048             if(e.shiftKey){
27049                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27050             }else{
27051                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27052             }
27053         }else if(k == e.ESC){
27054             ed.cancelEdit();
27055         }
27056         if(newCell){
27057             g.startEditing(newCell[0], newCell[1]);
27058         }
27059     }
27060 });
27061 /*
27062  * Based on:
27063  * Ext JS Library 1.1.1
27064  * Copyright(c) 2006-2007, Ext JS, LLC.
27065  *
27066  * Originally Released Under LGPL - original licence link has changed is not relivant.
27067  *
27068  * Fork - LGPL
27069  * <script type="text/javascript">
27070  */
27071  
27072 /**
27073  * @class Roo.bootstrap.PagingToolbar
27074  * @extends Roo.bootstrap.NavSimplebar
27075  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27076  * @constructor
27077  * Create a new PagingToolbar
27078  * @param {Object} config The config object
27079  * @param {Roo.data.Store} store
27080  */
27081 Roo.bootstrap.PagingToolbar = function(config)
27082 {
27083     // old args format still supported... - xtype is prefered..
27084         // created from xtype...
27085     
27086     this.ds = config.dataSource;
27087     
27088     if (config.store && !this.ds) {
27089         this.store= Roo.factory(config.store, Roo.data);
27090         this.ds = this.store;
27091         this.ds.xmodule = this.xmodule || false;
27092     }
27093     
27094     this.toolbarItems = [];
27095     if (config.items) {
27096         this.toolbarItems = config.items;
27097     }
27098     
27099     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27100     
27101     this.cursor = 0;
27102     
27103     if (this.ds) { 
27104         this.bind(this.ds);
27105     }
27106     
27107     if (Roo.bootstrap.version == 4) {
27108         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27109     } else {
27110         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27111     }
27112     
27113 };
27114
27115 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27116     /**
27117      * @cfg {Roo.data.Store} dataSource
27118      * The underlying data store providing the paged data
27119      */
27120     /**
27121      * @cfg {String/HTMLElement/Element} container
27122      * container The id or element that will contain the toolbar
27123      */
27124     /**
27125      * @cfg {Boolean} displayInfo
27126      * True to display the displayMsg (defaults to false)
27127      */
27128     /**
27129      * @cfg {Number} pageSize
27130      * The number of records to display per page (defaults to 20)
27131      */
27132     pageSize: 20,
27133     /**
27134      * @cfg {String} displayMsg
27135      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27136      */
27137     displayMsg : 'Displaying {0} - {1} of {2}',
27138     /**
27139      * @cfg {String} emptyMsg
27140      * The message to display when no records are found (defaults to "No data to display")
27141      */
27142     emptyMsg : 'No data to display',
27143     /**
27144      * Customizable piece of the default paging text (defaults to "Page")
27145      * @type String
27146      */
27147     beforePageText : "Page",
27148     /**
27149      * Customizable piece of the default paging text (defaults to "of %0")
27150      * @type String
27151      */
27152     afterPageText : "of {0}",
27153     /**
27154      * Customizable piece of the default paging text (defaults to "First Page")
27155      * @type String
27156      */
27157     firstText : "First Page",
27158     /**
27159      * Customizable piece of the default paging text (defaults to "Previous Page")
27160      * @type String
27161      */
27162     prevText : "Previous Page",
27163     /**
27164      * Customizable piece of the default paging text (defaults to "Next Page")
27165      * @type String
27166      */
27167     nextText : "Next Page",
27168     /**
27169      * Customizable piece of the default paging text (defaults to "Last Page")
27170      * @type String
27171      */
27172     lastText : "Last Page",
27173     /**
27174      * Customizable piece of the default paging text (defaults to "Refresh")
27175      * @type String
27176      */
27177     refreshText : "Refresh",
27178
27179     buttons : false,
27180     // private
27181     onRender : function(ct, position) 
27182     {
27183         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27184         this.navgroup.parentId = this.id;
27185         this.navgroup.onRender(this.el, null);
27186         // add the buttons to the navgroup
27187         
27188         if(this.displayInfo){
27189             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27190             this.displayEl = this.el.select('.x-paging-info', true).first();
27191 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27192 //            this.displayEl = navel.el.select('span',true).first();
27193         }
27194         
27195         var _this = this;
27196         
27197         if(this.buttons){
27198             Roo.each(_this.buttons, function(e){ // this might need to use render????
27199                Roo.factory(e).render(_this.el);
27200             });
27201         }
27202             
27203         Roo.each(_this.toolbarItems, function(e) {
27204             _this.navgroup.addItem(e);
27205         });
27206         
27207         
27208         this.first = this.navgroup.addItem({
27209             tooltip: this.firstText,
27210             cls: "prev btn-outline-secondary",
27211             html : ' <i class="fa fa-step-backward"></i>',
27212             disabled: true,
27213             preventDefault: true,
27214             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27215         });
27216         
27217         this.prev =  this.navgroup.addItem({
27218             tooltip: this.prevText,
27219             cls: "prev btn-outline-secondary",
27220             html : ' <i class="fa fa-backward"></i>',
27221             disabled: true,
27222             preventDefault: true,
27223             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27224         });
27225     //this.addSeparator();
27226         
27227         
27228         var field = this.navgroup.addItem( {
27229             tagtype : 'span',
27230             cls : 'x-paging-position  btn-outline-secondary',
27231              disabled: true,
27232             html : this.beforePageText  +
27233                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27234                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27235          } ); //?? escaped?
27236         
27237         this.field = field.el.select('input', true).first();
27238         this.field.on("keydown", this.onPagingKeydown, this);
27239         this.field.on("focus", function(){this.dom.select();});
27240     
27241     
27242         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27243         //this.field.setHeight(18);
27244         //this.addSeparator();
27245         this.next = this.navgroup.addItem({
27246             tooltip: this.nextText,
27247             cls: "next btn-outline-secondary",
27248             html : ' <i class="fa fa-forward"></i>',
27249             disabled: true,
27250             preventDefault: true,
27251             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27252         });
27253         this.last = this.navgroup.addItem({
27254             tooltip: this.lastText,
27255             html : ' <i class="fa fa-step-forward"></i>',
27256             cls: "next btn-outline-secondary",
27257             disabled: true,
27258             preventDefault: true,
27259             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27260         });
27261     //this.addSeparator();
27262         this.loading = this.navgroup.addItem({
27263             tooltip: this.refreshText,
27264             cls: "btn-outline-secondary",
27265             html : ' <i class="fa fa-refresh"></i>',
27266             preventDefault: true,
27267             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27268         });
27269         
27270     },
27271
27272     // private
27273     updateInfo : function(){
27274         if(this.displayEl){
27275             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27276             var msg = count == 0 ?
27277                 this.emptyMsg :
27278                 String.format(
27279                     this.displayMsg,
27280                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27281                 );
27282             this.displayEl.update(msg);
27283         }
27284     },
27285
27286     // private
27287     onLoad : function(ds, r, o)
27288     {
27289         this.cursor = o.params.start ? o.params.start : 0;
27290         
27291         var d = this.getPageData(),
27292             ap = d.activePage,
27293             ps = d.pages;
27294         
27295         
27296         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27297         this.field.dom.value = ap;
27298         this.first.setDisabled(ap == 1);
27299         this.prev.setDisabled(ap == 1);
27300         this.next.setDisabled(ap == ps);
27301         this.last.setDisabled(ap == ps);
27302         this.loading.enable();
27303         this.updateInfo();
27304     },
27305
27306     // private
27307     getPageData : function(){
27308         var total = this.ds.getTotalCount();
27309         return {
27310             total : total,
27311             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27312             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27313         };
27314     },
27315
27316     // private
27317     onLoadError : function(){
27318         this.loading.enable();
27319     },
27320
27321     // private
27322     onPagingKeydown : function(e){
27323         var k = e.getKey();
27324         var d = this.getPageData();
27325         if(k == e.RETURN){
27326             var v = this.field.dom.value, pageNum;
27327             if(!v || isNaN(pageNum = parseInt(v, 10))){
27328                 this.field.dom.value = d.activePage;
27329                 return;
27330             }
27331             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27332             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27333             e.stopEvent();
27334         }
27335         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))
27336         {
27337           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27338           this.field.dom.value = pageNum;
27339           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27340           e.stopEvent();
27341         }
27342         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27343         {
27344           var v = this.field.dom.value, pageNum; 
27345           var increment = (e.shiftKey) ? 10 : 1;
27346           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27347                 increment *= -1;
27348           }
27349           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27350             this.field.dom.value = d.activePage;
27351             return;
27352           }
27353           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27354           {
27355             this.field.dom.value = parseInt(v, 10) + increment;
27356             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27357             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27358           }
27359           e.stopEvent();
27360         }
27361     },
27362
27363     // private
27364     beforeLoad : function(){
27365         if(this.loading){
27366             this.loading.disable();
27367         }
27368     },
27369
27370     // private
27371     onClick : function(which){
27372         
27373         var ds = this.ds;
27374         if (!ds) {
27375             return;
27376         }
27377         
27378         switch(which){
27379             case "first":
27380                 ds.load({params:{start: 0, limit: this.pageSize}});
27381             break;
27382             case "prev":
27383                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27384             break;
27385             case "next":
27386                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27387             break;
27388             case "last":
27389                 var total = ds.getTotalCount();
27390                 var extra = total % this.pageSize;
27391                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27392                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27393             break;
27394             case "refresh":
27395                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27396             break;
27397         }
27398     },
27399
27400     /**
27401      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27402      * @param {Roo.data.Store} store The data store to unbind
27403      */
27404     unbind : function(ds){
27405         ds.un("beforeload", this.beforeLoad, this);
27406         ds.un("load", this.onLoad, this);
27407         ds.un("loadexception", this.onLoadError, this);
27408         ds.un("remove", this.updateInfo, this);
27409         ds.un("add", this.updateInfo, this);
27410         this.ds = undefined;
27411     },
27412
27413     /**
27414      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27415      * @param {Roo.data.Store} store The data store to bind
27416      */
27417     bind : function(ds){
27418         ds.on("beforeload", this.beforeLoad, this);
27419         ds.on("load", this.onLoad, this);
27420         ds.on("loadexception", this.onLoadError, this);
27421         ds.on("remove", this.updateInfo, this);
27422         ds.on("add", this.updateInfo, this);
27423         this.ds = ds;
27424     }
27425 });/*
27426  * - LGPL
27427  *
27428  * element
27429  * 
27430  */
27431
27432 /**
27433  * @class Roo.bootstrap.MessageBar
27434  * @extends Roo.bootstrap.Component
27435  * Bootstrap MessageBar class
27436  * @cfg {String} html contents of the MessageBar
27437  * @cfg {String} weight (info | success | warning | danger) default info
27438  * @cfg {String} beforeClass insert the bar before the given class
27439  * @cfg {Boolean} closable (true | false) default false
27440  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27441  * 
27442  * @constructor
27443  * Create a new Element
27444  * @param {Object} config The config object
27445  */
27446
27447 Roo.bootstrap.MessageBar = function(config){
27448     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27449 };
27450
27451 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27452     
27453     html: '',
27454     weight: 'info',
27455     closable: false,
27456     fixed: false,
27457     beforeClass: 'bootstrap-sticky-wrap',
27458     
27459     getAutoCreate : function(){
27460         
27461         var cfg = {
27462             tag: 'div',
27463             cls: 'alert alert-dismissable alert-' + this.weight,
27464             cn: [
27465                 {
27466                     tag: 'span',
27467                     cls: 'message',
27468                     html: this.html || ''
27469                 }
27470             ]
27471         };
27472         
27473         if(this.fixed){
27474             cfg.cls += ' alert-messages-fixed';
27475         }
27476         
27477         if(this.closable){
27478             cfg.cn.push({
27479                 tag: 'button',
27480                 cls: 'close',
27481                 html: 'x'
27482             });
27483         }
27484         
27485         return cfg;
27486     },
27487     
27488     onRender : function(ct, position)
27489     {
27490         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27491         
27492         if(!this.el){
27493             var cfg = Roo.apply({},  this.getAutoCreate());
27494             cfg.id = Roo.id();
27495             
27496             if (this.cls) {
27497                 cfg.cls += ' ' + this.cls;
27498             }
27499             if (this.style) {
27500                 cfg.style = this.style;
27501             }
27502             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27503             
27504             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27505         }
27506         
27507         this.el.select('>button.close').on('click', this.hide, this);
27508         
27509     },
27510     
27511     show : function()
27512     {
27513         if (!this.rendered) {
27514             this.render();
27515         }
27516         
27517         this.el.show();
27518         
27519         this.fireEvent('show', this);
27520         
27521     },
27522     
27523     hide : function()
27524     {
27525         if (!this.rendered) {
27526             this.render();
27527         }
27528         
27529         this.el.hide();
27530         
27531         this.fireEvent('hide', this);
27532     },
27533     
27534     update : function()
27535     {
27536 //        var e = this.el.dom.firstChild;
27537 //        
27538 //        if(this.closable){
27539 //            e = e.nextSibling;
27540 //        }
27541 //        
27542 //        e.data = this.html || '';
27543
27544         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27545     }
27546    
27547 });
27548
27549  
27550
27551      /*
27552  * - LGPL
27553  *
27554  * Graph
27555  * 
27556  */
27557
27558
27559 /**
27560  * @class Roo.bootstrap.Graph
27561  * @extends Roo.bootstrap.Component
27562  * Bootstrap Graph class
27563 > Prameters
27564  -sm {number} sm 4
27565  -md {number} md 5
27566  @cfg {String} graphtype  bar | vbar | pie
27567  @cfg {number} g_x coodinator | centre x (pie)
27568  @cfg {number} g_y coodinator | centre y (pie)
27569  @cfg {number} g_r radius (pie)
27570  @cfg {number} g_height height of the chart (respected by all elements in the set)
27571  @cfg {number} g_width width of the chart (respected by all elements in the set)
27572  @cfg {Object} title The title of the chart
27573     
27574  -{Array}  values
27575  -opts (object) options for the chart 
27576      o {
27577      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27578      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27579      o vgutter (number)
27580      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.
27581      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27582      o to
27583      o stretch (boolean)
27584      o }
27585  -opts (object) options for the pie
27586      o{
27587      o cut
27588      o startAngle (number)
27589      o endAngle (number)
27590      } 
27591  *
27592  * @constructor
27593  * Create a new Input
27594  * @param {Object} config The config object
27595  */
27596
27597 Roo.bootstrap.Graph = function(config){
27598     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27599     
27600     this.addEvents({
27601         // img events
27602         /**
27603          * @event click
27604          * The img click event for the img.
27605          * @param {Roo.EventObject} e
27606          */
27607         "click" : true
27608     });
27609 };
27610
27611 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27612     
27613     sm: 4,
27614     md: 5,
27615     graphtype: 'bar',
27616     g_height: 250,
27617     g_width: 400,
27618     g_x: 50,
27619     g_y: 50,
27620     g_r: 30,
27621     opts:{
27622         //g_colors: this.colors,
27623         g_type: 'soft',
27624         g_gutter: '20%'
27625
27626     },
27627     title : false,
27628
27629     getAutoCreate : function(){
27630         
27631         var cfg = {
27632             tag: 'div',
27633             html : null
27634         };
27635         
27636         
27637         return  cfg;
27638     },
27639
27640     onRender : function(ct,position){
27641         
27642         
27643         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27644         
27645         if (typeof(Raphael) == 'undefined') {
27646             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27647             return;
27648         }
27649         
27650         this.raphael = Raphael(this.el.dom);
27651         
27652                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27653                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27654                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27655                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27656                 /*
27657                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27658                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27659                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27660                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27661                 
27662                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27663                 r.barchart(330, 10, 300, 220, data1);
27664                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27665                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27666                 */
27667                 
27668                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27669                 // r.barchart(30, 30, 560, 250,  xdata, {
27670                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27671                 //     axis : "0 0 1 1",
27672                 //     axisxlabels :  xdata
27673                 //     //yvalues : cols,
27674                    
27675                 // });
27676 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27677 //        
27678 //        this.load(null,xdata,{
27679 //                axis : "0 0 1 1",
27680 //                axisxlabels :  xdata
27681 //                });
27682
27683     },
27684
27685     load : function(graphtype,xdata,opts)
27686     {
27687         this.raphael.clear();
27688         if(!graphtype) {
27689             graphtype = this.graphtype;
27690         }
27691         if(!opts){
27692             opts = this.opts;
27693         }
27694         var r = this.raphael,
27695             fin = function () {
27696                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27697             },
27698             fout = function () {
27699                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27700             },
27701             pfin = function() {
27702                 this.sector.stop();
27703                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27704
27705                 if (this.label) {
27706                     this.label[0].stop();
27707                     this.label[0].attr({ r: 7.5 });
27708                     this.label[1].attr({ "font-weight": 800 });
27709                 }
27710             },
27711             pfout = function() {
27712                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27713
27714                 if (this.label) {
27715                     this.label[0].animate({ r: 5 }, 500, "bounce");
27716                     this.label[1].attr({ "font-weight": 400 });
27717                 }
27718             };
27719
27720         switch(graphtype){
27721             case 'bar':
27722                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27723                 break;
27724             case 'hbar':
27725                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27726                 break;
27727             case 'pie':
27728 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27729 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27730 //            
27731                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27732                 
27733                 break;
27734
27735         }
27736         
27737         if(this.title){
27738             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27739         }
27740         
27741     },
27742     
27743     setTitle: function(o)
27744     {
27745         this.title = o;
27746     },
27747     
27748     initEvents: function() {
27749         
27750         if(!this.href){
27751             this.el.on('click', this.onClick, this);
27752         }
27753     },
27754     
27755     onClick : function(e)
27756     {
27757         Roo.log('img onclick');
27758         this.fireEvent('click', this, e);
27759     }
27760    
27761 });
27762
27763  
27764 /*
27765  * - LGPL
27766  *
27767  * numberBox
27768  * 
27769  */
27770 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27771
27772 /**
27773  * @class Roo.bootstrap.dash.NumberBox
27774  * @extends Roo.bootstrap.Component
27775  * Bootstrap NumberBox class
27776  * @cfg {String} headline Box headline
27777  * @cfg {String} content Box content
27778  * @cfg {String} icon Box icon
27779  * @cfg {String} footer Footer text
27780  * @cfg {String} fhref Footer href
27781  * 
27782  * @constructor
27783  * Create a new NumberBox
27784  * @param {Object} config The config object
27785  */
27786
27787
27788 Roo.bootstrap.dash.NumberBox = function(config){
27789     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27790     
27791 };
27792
27793 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27794     
27795     headline : '',
27796     content : '',
27797     icon : '',
27798     footer : '',
27799     fhref : '',
27800     ficon : '',
27801     
27802     getAutoCreate : function(){
27803         
27804         var cfg = {
27805             tag : 'div',
27806             cls : 'small-box ',
27807             cn : [
27808                 {
27809                     tag : 'div',
27810                     cls : 'inner',
27811                     cn :[
27812                         {
27813                             tag : 'h3',
27814                             cls : 'roo-headline',
27815                             html : this.headline
27816                         },
27817                         {
27818                             tag : 'p',
27819                             cls : 'roo-content',
27820                             html : this.content
27821                         }
27822                     ]
27823                 }
27824             ]
27825         };
27826         
27827         if(this.icon){
27828             cfg.cn.push({
27829                 tag : 'div',
27830                 cls : 'icon',
27831                 cn :[
27832                     {
27833                         tag : 'i',
27834                         cls : 'ion ' + this.icon
27835                     }
27836                 ]
27837             });
27838         }
27839         
27840         if(this.footer){
27841             var footer = {
27842                 tag : 'a',
27843                 cls : 'small-box-footer',
27844                 href : this.fhref || '#',
27845                 html : this.footer
27846             };
27847             
27848             cfg.cn.push(footer);
27849             
27850         }
27851         
27852         return  cfg;
27853     },
27854
27855     onRender : function(ct,position){
27856         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27857
27858
27859        
27860                 
27861     },
27862
27863     setHeadline: function (value)
27864     {
27865         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27866     },
27867     
27868     setFooter: function (value, href)
27869     {
27870         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27871         
27872         if(href){
27873             this.el.select('a.small-box-footer',true).first().attr('href', href);
27874         }
27875         
27876     },
27877
27878     setContent: function (value)
27879     {
27880         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27881     },
27882
27883     initEvents: function() 
27884     {   
27885         
27886     }
27887     
27888 });
27889
27890  
27891 /*
27892  * - LGPL
27893  *
27894  * TabBox
27895  * 
27896  */
27897 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27898
27899 /**
27900  * @class Roo.bootstrap.dash.TabBox
27901  * @extends Roo.bootstrap.Component
27902  * Bootstrap TabBox class
27903  * @cfg {String} title Title of the TabBox
27904  * @cfg {String} icon Icon of the TabBox
27905  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27906  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27907  * 
27908  * @constructor
27909  * Create a new TabBox
27910  * @param {Object} config The config object
27911  */
27912
27913
27914 Roo.bootstrap.dash.TabBox = function(config){
27915     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27916     this.addEvents({
27917         // raw events
27918         /**
27919          * @event addpane
27920          * When a pane is added
27921          * @param {Roo.bootstrap.dash.TabPane} pane
27922          */
27923         "addpane" : true,
27924         /**
27925          * @event activatepane
27926          * When a pane is activated
27927          * @param {Roo.bootstrap.dash.TabPane} pane
27928          */
27929         "activatepane" : true
27930         
27931          
27932     });
27933     
27934     this.panes = [];
27935 };
27936
27937 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27938
27939     title : '',
27940     icon : false,
27941     showtabs : true,
27942     tabScrollable : false,
27943     
27944     getChildContainer : function()
27945     {
27946         return this.el.select('.tab-content', true).first();
27947     },
27948     
27949     getAutoCreate : function(){
27950         
27951         var header = {
27952             tag: 'li',
27953             cls: 'pull-left header',
27954             html: this.title,
27955             cn : []
27956         };
27957         
27958         if(this.icon){
27959             header.cn.push({
27960                 tag: 'i',
27961                 cls: 'fa ' + this.icon
27962             });
27963         }
27964         
27965         var h = {
27966             tag: 'ul',
27967             cls: 'nav nav-tabs pull-right',
27968             cn: [
27969                 header
27970             ]
27971         };
27972         
27973         if(this.tabScrollable){
27974             h = {
27975                 tag: 'div',
27976                 cls: 'tab-header',
27977                 cn: [
27978                     {
27979                         tag: 'ul',
27980                         cls: 'nav nav-tabs pull-right',
27981                         cn: [
27982                             header
27983                         ]
27984                     }
27985                 ]
27986             };
27987         }
27988         
27989         var cfg = {
27990             tag: 'div',
27991             cls: 'nav-tabs-custom',
27992             cn: [
27993                 h,
27994                 {
27995                     tag: 'div',
27996                     cls: 'tab-content no-padding',
27997                     cn: []
27998                 }
27999             ]
28000         };
28001
28002         return  cfg;
28003     },
28004     initEvents : function()
28005     {
28006         //Roo.log('add add pane handler');
28007         this.on('addpane', this.onAddPane, this);
28008     },
28009      /**
28010      * Updates the box title
28011      * @param {String} html to set the title to.
28012      */
28013     setTitle : function(value)
28014     {
28015         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28016     },
28017     onAddPane : function(pane)
28018     {
28019         this.panes.push(pane);
28020         //Roo.log('addpane');
28021         //Roo.log(pane);
28022         // tabs are rendere left to right..
28023         if(!this.showtabs){
28024             return;
28025         }
28026         
28027         var ctr = this.el.select('.nav-tabs', true).first();
28028          
28029          
28030         var existing = ctr.select('.nav-tab',true);
28031         var qty = existing.getCount();;
28032         
28033         
28034         var tab = ctr.createChild({
28035             tag : 'li',
28036             cls : 'nav-tab' + (qty ? '' : ' active'),
28037             cn : [
28038                 {
28039                     tag : 'a',
28040                     href:'#',
28041                     html : pane.title
28042                 }
28043             ]
28044         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28045         pane.tab = tab;
28046         
28047         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28048         if (!qty) {
28049             pane.el.addClass('active');
28050         }
28051         
28052                 
28053     },
28054     onTabClick : function(ev,un,ob,pane)
28055     {
28056         //Roo.log('tab - prev default');
28057         ev.preventDefault();
28058         
28059         
28060         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28061         pane.tab.addClass('active');
28062         //Roo.log(pane.title);
28063         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28064         // technically we should have a deactivate event.. but maybe add later.
28065         // and it should not de-activate the selected tab...
28066         this.fireEvent('activatepane', pane);
28067         pane.el.addClass('active');
28068         pane.fireEvent('activate');
28069         
28070         
28071     },
28072     
28073     getActivePane : function()
28074     {
28075         var r = false;
28076         Roo.each(this.panes, function(p) {
28077             if(p.el.hasClass('active')){
28078                 r = p;
28079                 return false;
28080             }
28081             
28082             return;
28083         });
28084         
28085         return r;
28086     }
28087     
28088     
28089 });
28090
28091  
28092 /*
28093  * - LGPL
28094  *
28095  * Tab pane
28096  * 
28097  */
28098 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28099 /**
28100  * @class Roo.bootstrap.TabPane
28101  * @extends Roo.bootstrap.Component
28102  * Bootstrap TabPane class
28103  * @cfg {Boolean} active (false | true) Default false
28104  * @cfg {String} title title of panel
28105
28106  * 
28107  * @constructor
28108  * Create a new TabPane
28109  * @param {Object} config The config object
28110  */
28111
28112 Roo.bootstrap.dash.TabPane = function(config){
28113     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28114     
28115     this.addEvents({
28116         // raw events
28117         /**
28118          * @event activate
28119          * When a pane is activated
28120          * @param {Roo.bootstrap.dash.TabPane} pane
28121          */
28122         "activate" : true
28123          
28124     });
28125 };
28126
28127 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28128     
28129     active : false,
28130     title : '',
28131     
28132     // the tabBox that this is attached to.
28133     tab : false,
28134      
28135     getAutoCreate : function() 
28136     {
28137         var cfg = {
28138             tag: 'div',
28139             cls: 'tab-pane'
28140         };
28141         
28142         if(this.active){
28143             cfg.cls += ' active';
28144         }
28145         
28146         return cfg;
28147     },
28148     initEvents  : function()
28149     {
28150         //Roo.log('trigger add pane handler');
28151         this.parent().fireEvent('addpane', this)
28152     },
28153     
28154      /**
28155      * Updates the tab title 
28156      * @param {String} html to set the title to.
28157      */
28158     setTitle: function(str)
28159     {
28160         if (!this.tab) {
28161             return;
28162         }
28163         this.title = str;
28164         this.tab.select('a', true).first().dom.innerHTML = str;
28165         
28166     }
28167     
28168     
28169     
28170 });
28171
28172  
28173
28174
28175  /*
28176  * - LGPL
28177  *
28178  * menu
28179  * 
28180  */
28181 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28182
28183 /**
28184  * @class Roo.bootstrap.menu.Menu
28185  * @extends Roo.bootstrap.Component
28186  * Bootstrap Menu class - container for Menu
28187  * @cfg {String} html Text of the menu
28188  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28189  * @cfg {String} icon Font awesome icon
28190  * @cfg {String} pos Menu align to (top | bottom) default bottom
28191  * 
28192  * 
28193  * @constructor
28194  * Create a new Menu
28195  * @param {Object} config The config object
28196  */
28197
28198
28199 Roo.bootstrap.menu.Menu = function(config){
28200     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28201     
28202     this.addEvents({
28203         /**
28204          * @event beforeshow
28205          * Fires before this menu is displayed
28206          * @param {Roo.bootstrap.menu.Menu} this
28207          */
28208         beforeshow : true,
28209         /**
28210          * @event beforehide
28211          * Fires before this menu is hidden
28212          * @param {Roo.bootstrap.menu.Menu} this
28213          */
28214         beforehide : true,
28215         /**
28216          * @event show
28217          * Fires after this menu is displayed
28218          * @param {Roo.bootstrap.menu.Menu} this
28219          */
28220         show : true,
28221         /**
28222          * @event hide
28223          * Fires after this menu is hidden
28224          * @param {Roo.bootstrap.menu.Menu} this
28225          */
28226         hide : true,
28227         /**
28228          * @event click
28229          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28230          * @param {Roo.bootstrap.menu.Menu} this
28231          * @param {Roo.EventObject} e
28232          */
28233         click : true
28234     });
28235     
28236 };
28237
28238 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28239     
28240     submenu : false,
28241     html : '',
28242     weight : 'default',
28243     icon : false,
28244     pos : 'bottom',
28245     
28246     
28247     getChildContainer : function() {
28248         if(this.isSubMenu){
28249             return this.el;
28250         }
28251         
28252         return this.el.select('ul.dropdown-menu', true).first();  
28253     },
28254     
28255     getAutoCreate : function()
28256     {
28257         var text = [
28258             {
28259                 tag : 'span',
28260                 cls : 'roo-menu-text',
28261                 html : this.html
28262             }
28263         ];
28264         
28265         if(this.icon){
28266             text.unshift({
28267                 tag : 'i',
28268                 cls : 'fa ' + this.icon
28269             })
28270         }
28271         
28272         
28273         var cfg = {
28274             tag : 'div',
28275             cls : 'btn-group',
28276             cn : [
28277                 {
28278                     tag : 'button',
28279                     cls : 'dropdown-button btn btn-' + this.weight,
28280                     cn : text
28281                 },
28282                 {
28283                     tag : 'button',
28284                     cls : 'dropdown-toggle btn btn-' + this.weight,
28285                     cn : [
28286                         {
28287                             tag : 'span',
28288                             cls : 'caret'
28289                         }
28290                     ]
28291                 },
28292                 {
28293                     tag : 'ul',
28294                     cls : 'dropdown-menu'
28295                 }
28296             ]
28297             
28298         };
28299         
28300         if(this.pos == 'top'){
28301             cfg.cls += ' dropup';
28302         }
28303         
28304         if(this.isSubMenu){
28305             cfg = {
28306                 tag : 'ul',
28307                 cls : 'dropdown-menu'
28308             }
28309         }
28310         
28311         return cfg;
28312     },
28313     
28314     onRender : function(ct, position)
28315     {
28316         this.isSubMenu = ct.hasClass('dropdown-submenu');
28317         
28318         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28319     },
28320     
28321     initEvents : function() 
28322     {
28323         if(this.isSubMenu){
28324             return;
28325         }
28326         
28327         this.hidden = true;
28328         
28329         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28330         this.triggerEl.on('click', this.onTriggerPress, this);
28331         
28332         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28333         this.buttonEl.on('click', this.onClick, this);
28334         
28335     },
28336     
28337     list : function()
28338     {
28339         if(this.isSubMenu){
28340             return this.el;
28341         }
28342         
28343         return this.el.select('ul.dropdown-menu', true).first();
28344     },
28345     
28346     onClick : function(e)
28347     {
28348         this.fireEvent("click", this, e);
28349     },
28350     
28351     onTriggerPress  : function(e)
28352     {   
28353         if (this.isVisible()) {
28354             this.hide();
28355         } else {
28356             this.show();
28357         }
28358     },
28359     
28360     isVisible : function(){
28361         return !this.hidden;
28362     },
28363     
28364     show : function()
28365     {
28366         this.fireEvent("beforeshow", this);
28367         
28368         this.hidden = false;
28369         this.el.addClass('open');
28370         
28371         Roo.get(document).on("mouseup", this.onMouseUp, this);
28372         
28373         this.fireEvent("show", this);
28374         
28375         
28376     },
28377     
28378     hide : function()
28379     {
28380         this.fireEvent("beforehide", this);
28381         
28382         this.hidden = true;
28383         this.el.removeClass('open');
28384         
28385         Roo.get(document).un("mouseup", this.onMouseUp);
28386         
28387         this.fireEvent("hide", this);
28388     },
28389     
28390     onMouseUp : function()
28391     {
28392         this.hide();
28393     }
28394     
28395 });
28396
28397  
28398  /*
28399  * - LGPL
28400  *
28401  * menu item
28402  * 
28403  */
28404 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28405
28406 /**
28407  * @class Roo.bootstrap.menu.Item
28408  * @extends Roo.bootstrap.Component
28409  * Bootstrap MenuItem class
28410  * @cfg {Boolean} submenu (true | false) default false
28411  * @cfg {String} html text of the item
28412  * @cfg {String} href the link
28413  * @cfg {Boolean} disable (true | false) default false
28414  * @cfg {Boolean} preventDefault (true | false) default true
28415  * @cfg {String} icon Font awesome icon
28416  * @cfg {String} pos Submenu align to (left | right) default right 
28417  * 
28418  * 
28419  * @constructor
28420  * Create a new Item
28421  * @param {Object} config The config object
28422  */
28423
28424
28425 Roo.bootstrap.menu.Item = function(config){
28426     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28427     this.addEvents({
28428         /**
28429          * @event mouseover
28430          * Fires when the mouse is hovering over this menu
28431          * @param {Roo.bootstrap.menu.Item} this
28432          * @param {Roo.EventObject} e
28433          */
28434         mouseover : true,
28435         /**
28436          * @event mouseout
28437          * Fires when the mouse exits this menu
28438          * @param {Roo.bootstrap.menu.Item} this
28439          * @param {Roo.EventObject} e
28440          */
28441         mouseout : true,
28442         // raw events
28443         /**
28444          * @event click
28445          * The raw click event for the entire grid.
28446          * @param {Roo.EventObject} e
28447          */
28448         click : true
28449     });
28450 };
28451
28452 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28453     
28454     submenu : false,
28455     href : '',
28456     html : '',
28457     preventDefault: true,
28458     disable : false,
28459     icon : false,
28460     pos : 'right',
28461     
28462     getAutoCreate : function()
28463     {
28464         var text = [
28465             {
28466                 tag : 'span',
28467                 cls : 'roo-menu-item-text',
28468                 html : this.html
28469             }
28470         ];
28471         
28472         if(this.icon){
28473             text.unshift({
28474                 tag : 'i',
28475                 cls : 'fa ' + this.icon
28476             })
28477         }
28478         
28479         var cfg = {
28480             tag : 'li',
28481             cn : [
28482                 {
28483                     tag : 'a',
28484                     href : this.href || '#',
28485                     cn : text
28486                 }
28487             ]
28488         };
28489         
28490         if(this.disable){
28491             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28492         }
28493         
28494         if(this.submenu){
28495             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28496             
28497             if(this.pos == 'left'){
28498                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28499             }
28500         }
28501         
28502         return cfg;
28503     },
28504     
28505     initEvents : function() 
28506     {
28507         this.el.on('mouseover', this.onMouseOver, this);
28508         this.el.on('mouseout', this.onMouseOut, this);
28509         
28510         this.el.select('a', true).first().on('click', this.onClick, this);
28511         
28512     },
28513     
28514     onClick : function(e)
28515     {
28516         if(this.preventDefault){
28517             e.preventDefault();
28518         }
28519         
28520         this.fireEvent("click", this, e);
28521     },
28522     
28523     onMouseOver : function(e)
28524     {
28525         if(this.submenu && this.pos == 'left'){
28526             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28527         }
28528         
28529         this.fireEvent("mouseover", this, e);
28530     },
28531     
28532     onMouseOut : function(e)
28533     {
28534         this.fireEvent("mouseout", this, e);
28535     }
28536 });
28537
28538  
28539
28540  /*
28541  * - LGPL
28542  *
28543  * menu separator
28544  * 
28545  */
28546 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28547
28548 /**
28549  * @class Roo.bootstrap.menu.Separator
28550  * @extends Roo.bootstrap.Component
28551  * Bootstrap Separator class
28552  * 
28553  * @constructor
28554  * Create a new Separator
28555  * @param {Object} config The config object
28556  */
28557
28558
28559 Roo.bootstrap.menu.Separator = function(config){
28560     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28561 };
28562
28563 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28564     
28565     getAutoCreate : function(){
28566         var cfg = {
28567             tag : 'li',
28568             cls: 'divider'
28569         };
28570         
28571         return cfg;
28572     }
28573    
28574 });
28575
28576  
28577
28578  /*
28579  * - LGPL
28580  *
28581  * Tooltip
28582  * 
28583  */
28584
28585 /**
28586  * @class Roo.bootstrap.Tooltip
28587  * Bootstrap Tooltip class
28588  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28589  * to determine which dom element triggers the tooltip.
28590  * 
28591  * It needs to add support for additional attributes like tooltip-position
28592  * 
28593  * @constructor
28594  * Create a new Toolti
28595  * @param {Object} config The config object
28596  */
28597
28598 Roo.bootstrap.Tooltip = function(config){
28599     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28600     
28601     this.alignment = Roo.bootstrap.Tooltip.alignment;
28602     
28603     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28604         this.alignment = config.alignment;
28605     }
28606     
28607 };
28608
28609 Roo.apply(Roo.bootstrap.Tooltip, {
28610     /**
28611      * @function init initialize tooltip monitoring.
28612      * @static
28613      */
28614     currentEl : false,
28615     currentTip : false,
28616     currentRegion : false,
28617     
28618     //  init : delay?
28619     
28620     init : function()
28621     {
28622         Roo.get(document).on('mouseover', this.enter ,this);
28623         Roo.get(document).on('mouseout', this.leave, this);
28624          
28625         
28626         this.currentTip = new Roo.bootstrap.Tooltip();
28627     },
28628     
28629     enter : function(ev)
28630     {
28631         var dom = ev.getTarget();
28632         
28633         //Roo.log(['enter',dom]);
28634         var el = Roo.fly(dom);
28635         if (this.currentEl) {
28636             //Roo.log(dom);
28637             //Roo.log(this.currentEl);
28638             //Roo.log(this.currentEl.contains(dom));
28639             if (this.currentEl == el) {
28640                 return;
28641             }
28642             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28643                 return;
28644             }
28645
28646         }
28647         
28648         if (this.currentTip.el) {
28649             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28650         }    
28651         //Roo.log(ev);
28652         
28653         if(!el || el.dom == document){
28654             return;
28655         }
28656         
28657         var bindEl = el;
28658         
28659         // you can not look for children, as if el is the body.. then everythign is the child..
28660         if (!el.attr('tooltip')) { //
28661             if (!el.select("[tooltip]").elements.length) {
28662                 return;
28663             }
28664             // is the mouse over this child...?
28665             bindEl = el.select("[tooltip]").first();
28666             var xy = ev.getXY();
28667             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28668                 //Roo.log("not in region.");
28669                 return;
28670             }
28671             //Roo.log("child element over..");
28672             
28673         }
28674         this.currentEl = bindEl;
28675         this.currentTip.bind(bindEl);
28676         this.currentRegion = Roo.lib.Region.getRegion(dom);
28677         this.currentTip.enter();
28678         
28679     },
28680     leave : function(ev)
28681     {
28682         var dom = ev.getTarget();
28683         //Roo.log(['leave',dom]);
28684         if (!this.currentEl) {
28685             return;
28686         }
28687         
28688         
28689         if (dom != this.currentEl.dom) {
28690             return;
28691         }
28692         var xy = ev.getXY();
28693         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28694             return;
28695         }
28696         // only activate leave if mouse cursor is outside... bounding box..
28697         
28698         
28699         
28700         
28701         if (this.currentTip) {
28702             this.currentTip.leave();
28703         }
28704         //Roo.log('clear currentEl');
28705         this.currentEl = false;
28706         
28707         
28708     },
28709     alignment : {
28710         'left' : ['r-l', [-2,0], 'right'],
28711         'right' : ['l-r', [2,0], 'left'],
28712         'bottom' : ['t-b', [0,2], 'top'],
28713         'top' : [ 'b-t', [0,-2], 'bottom']
28714     }
28715     
28716 });
28717
28718
28719 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28720     
28721     
28722     bindEl : false,
28723     
28724     delay : null, // can be { show : 300 , hide: 500}
28725     
28726     timeout : null,
28727     
28728     hoverState : null, //???
28729     
28730     placement : 'bottom', 
28731     
28732     alignment : false,
28733     
28734     getAutoCreate : function(){
28735     
28736         var cfg = {
28737            cls : 'tooltip',   
28738            role : 'tooltip',
28739            cn : [
28740                 {
28741                     cls : 'tooltip-arrow arrow'
28742                 },
28743                 {
28744                     cls : 'tooltip-inner'
28745                 }
28746            ]
28747         };
28748         
28749         return cfg;
28750     },
28751     bind : function(el)
28752     {
28753         this.bindEl = el;
28754     },
28755     
28756     initEvents : function()
28757     {
28758         this.arrowEl = this.el.select('.arrow', true).first();
28759         this.innerEl = this.el.select('.tooltip-inner', true).first();
28760     },
28761     
28762     enter : function () {
28763        
28764         if (this.timeout != null) {
28765             clearTimeout(this.timeout);
28766         }
28767         
28768         this.hoverState = 'in';
28769          //Roo.log("enter - show");
28770         if (!this.delay || !this.delay.show) {
28771             this.show();
28772             return;
28773         }
28774         var _t = this;
28775         this.timeout = setTimeout(function () {
28776             if (_t.hoverState == 'in') {
28777                 _t.show();
28778             }
28779         }, this.delay.show);
28780     },
28781     leave : function()
28782     {
28783         clearTimeout(this.timeout);
28784     
28785         this.hoverState = 'out';
28786          if (!this.delay || !this.delay.hide) {
28787             this.hide();
28788             return;
28789         }
28790        
28791         var _t = this;
28792         this.timeout = setTimeout(function () {
28793             //Roo.log("leave - timeout");
28794             
28795             if (_t.hoverState == 'out') {
28796                 _t.hide();
28797                 Roo.bootstrap.Tooltip.currentEl = false;
28798             }
28799         }, delay);
28800     },
28801     
28802     show : function (msg)
28803     {
28804         if (!this.el) {
28805             this.render(document.body);
28806         }
28807         // set content.
28808         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28809         
28810         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28811         
28812         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28813         
28814         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28815                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28816         
28817         var placement = typeof this.placement == 'function' ?
28818             this.placement.call(this, this.el, on_el) :
28819             this.placement;
28820             
28821         var autoToken = /\s?auto?\s?/i;
28822         var autoPlace = autoToken.test(placement);
28823         if (autoPlace) {
28824             placement = placement.replace(autoToken, '') || 'top';
28825         }
28826         
28827         //this.el.detach()
28828         //this.el.setXY([0,0]);
28829         this.el.show();
28830         //this.el.dom.style.display='block';
28831         
28832         //this.el.appendTo(on_el);
28833         
28834         var p = this.getPosition();
28835         var box = this.el.getBox();
28836         
28837         if (autoPlace) {
28838             // fixme..
28839         }
28840         
28841         var align = this.alignment[placement];
28842         
28843         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28844         
28845         if(placement == 'top' || placement == 'bottom'){
28846             if(xy[0] < 0){
28847                 placement = 'right';
28848             }
28849             
28850             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28851                 placement = 'left';
28852             }
28853             
28854             var scroll = Roo.select('body', true).first().getScroll();
28855             
28856             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28857                 placement = 'top';
28858             }
28859             
28860             align = this.alignment[placement];
28861             
28862             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28863             
28864         }
28865         
28866         this.el.alignTo(this.bindEl, align[0],align[1]);
28867         //var arrow = this.el.select('.arrow',true).first();
28868         //arrow.set(align[2], 
28869         
28870         this.el.addClass(placement);
28871         this.el.addClass("bs-tooltip-"+ placement);
28872         
28873         this.el.addClass('in fade show');
28874         
28875         this.hoverState = null;
28876         
28877         if (this.el.hasClass('fade')) {
28878             // fade it?
28879         }
28880         
28881         
28882         
28883         
28884         
28885     },
28886     hide : function()
28887     {
28888          
28889         if (!this.el) {
28890             return;
28891         }
28892         //this.el.setXY([0,0]);
28893         this.el.removeClass(['show', 'in']);
28894         //this.el.hide();
28895         
28896     }
28897     
28898 });
28899  
28900
28901  /*
28902  * - LGPL
28903  *
28904  * Location Picker
28905  * 
28906  */
28907
28908 /**
28909  * @class Roo.bootstrap.LocationPicker
28910  * @extends Roo.bootstrap.Component
28911  * Bootstrap LocationPicker class
28912  * @cfg {Number} latitude Position when init default 0
28913  * @cfg {Number} longitude Position when init default 0
28914  * @cfg {Number} zoom default 15
28915  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28916  * @cfg {Boolean} mapTypeControl default false
28917  * @cfg {Boolean} disableDoubleClickZoom default false
28918  * @cfg {Boolean} scrollwheel default true
28919  * @cfg {Boolean} streetViewControl default false
28920  * @cfg {Number} radius default 0
28921  * @cfg {String} locationName
28922  * @cfg {Boolean} draggable default true
28923  * @cfg {Boolean} enableAutocomplete default false
28924  * @cfg {Boolean} enableReverseGeocode default true
28925  * @cfg {String} markerTitle
28926  * 
28927  * @constructor
28928  * Create a new LocationPicker
28929  * @param {Object} config The config object
28930  */
28931
28932
28933 Roo.bootstrap.LocationPicker = function(config){
28934     
28935     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28936     
28937     this.addEvents({
28938         /**
28939          * @event initial
28940          * Fires when the picker initialized.
28941          * @param {Roo.bootstrap.LocationPicker} this
28942          * @param {Google Location} location
28943          */
28944         initial : true,
28945         /**
28946          * @event positionchanged
28947          * Fires when the picker position changed.
28948          * @param {Roo.bootstrap.LocationPicker} this
28949          * @param {Google Location} location
28950          */
28951         positionchanged : true,
28952         /**
28953          * @event resize
28954          * Fires when the map resize.
28955          * @param {Roo.bootstrap.LocationPicker} this
28956          */
28957         resize : true,
28958         /**
28959          * @event show
28960          * Fires when the map show.
28961          * @param {Roo.bootstrap.LocationPicker} this
28962          */
28963         show : true,
28964         /**
28965          * @event hide
28966          * Fires when the map hide.
28967          * @param {Roo.bootstrap.LocationPicker} this
28968          */
28969         hide : true,
28970         /**
28971          * @event mapClick
28972          * Fires when click the map.
28973          * @param {Roo.bootstrap.LocationPicker} this
28974          * @param {Map event} e
28975          */
28976         mapClick : true,
28977         /**
28978          * @event mapRightClick
28979          * Fires when right click the map.
28980          * @param {Roo.bootstrap.LocationPicker} this
28981          * @param {Map event} e
28982          */
28983         mapRightClick : true,
28984         /**
28985          * @event markerClick
28986          * Fires when click the marker.
28987          * @param {Roo.bootstrap.LocationPicker} this
28988          * @param {Map event} e
28989          */
28990         markerClick : true,
28991         /**
28992          * @event markerRightClick
28993          * Fires when right click the marker.
28994          * @param {Roo.bootstrap.LocationPicker} this
28995          * @param {Map event} e
28996          */
28997         markerRightClick : true,
28998         /**
28999          * @event OverlayViewDraw
29000          * Fires when OverlayView Draw
29001          * @param {Roo.bootstrap.LocationPicker} this
29002          */
29003         OverlayViewDraw : true,
29004         /**
29005          * @event OverlayViewOnAdd
29006          * Fires when OverlayView Draw
29007          * @param {Roo.bootstrap.LocationPicker} this
29008          */
29009         OverlayViewOnAdd : true,
29010         /**
29011          * @event OverlayViewOnRemove
29012          * Fires when OverlayView Draw
29013          * @param {Roo.bootstrap.LocationPicker} this
29014          */
29015         OverlayViewOnRemove : true,
29016         /**
29017          * @event OverlayViewShow
29018          * Fires when OverlayView Draw
29019          * @param {Roo.bootstrap.LocationPicker} this
29020          * @param {Pixel} cpx
29021          */
29022         OverlayViewShow : true,
29023         /**
29024          * @event OverlayViewHide
29025          * Fires when OverlayView Draw
29026          * @param {Roo.bootstrap.LocationPicker} this
29027          */
29028         OverlayViewHide : true,
29029         /**
29030          * @event loadexception
29031          * Fires when load google lib failed.
29032          * @param {Roo.bootstrap.LocationPicker} this
29033          */
29034         loadexception : true
29035     });
29036         
29037 };
29038
29039 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29040     
29041     gMapContext: false,
29042     
29043     latitude: 0,
29044     longitude: 0,
29045     zoom: 15,
29046     mapTypeId: false,
29047     mapTypeControl: false,
29048     disableDoubleClickZoom: false,
29049     scrollwheel: true,
29050     streetViewControl: false,
29051     radius: 0,
29052     locationName: '',
29053     draggable: true,
29054     enableAutocomplete: false,
29055     enableReverseGeocode: true,
29056     markerTitle: '',
29057     
29058     getAutoCreate: function()
29059     {
29060
29061         var cfg = {
29062             tag: 'div',
29063             cls: 'roo-location-picker'
29064         };
29065         
29066         return cfg
29067     },
29068     
29069     initEvents: function(ct, position)
29070     {       
29071         if(!this.el.getWidth() || this.isApplied()){
29072             return;
29073         }
29074         
29075         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29076         
29077         this.initial();
29078     },
29079     
29080     initial: function()
29081     {
29082         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29083             this.fireEvent('loadexception', this);
29084             return;
29085         }
29086         
29087         if(!this.mapTypeId){
29088             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29089         }
29090         
29091         this.gMapContext = this.GMapContext();
29092         
29093         this.initOverlayView();
29094         
29095         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29096         
29097         var _this = this;
29098                 
29099         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29100             _this.setPosition(_this.gMapContext.marker.position);
29101         });
29102         
29103         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29104             _this.fireEvent('mapClick', this, event);
29105             
29106         });
29107
29108         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29109             _this.fireEvent('mapRightClick', this, event);
29110             
29111         });
29112         
29113         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29114             _this.fireEvent('markerClick', this, event);
29115             
29116         });
29117
29118         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29119             _this.fireEvent('markerRightClick', this, event);
29120             
29121         });
29122         
29123         this.setPosition(this.gMapContext.location);
29124         
29125         this.fireEvent('initial', this, this.gMapContext.location);
29126     },
29127     
29128     initOverlayView: function()
29129     {
29130         var _this = this;
29131         
29132         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29133             
29134             draw: function()
29135             {
29136                 _this.fireEvent('OverlayViewDraw', _this);
29137             },
29138             
29139             onAdd: function()
29140             {
29141                 _this.fireEvent('OverlayViewOnAdd', _this);
29142             },
29143             
29144             onRemove: function()
29145             {
29146                 _this.fireEvent('OverlayViewOnRemove', _this);
29147             },
29148             
29149             show: function(cpx)
29150             {
29151                 _this.fireEvent('OverlayViewShow', _this, cpx);
29152             },
29153             
29154             hide: function()
29155             {
29156                 _this.fireEvent('OverlayViewHide', _this);
29157             }
29158             
29159         });
29160     },
29161     
29162     fromLatLngToContainerPixel: function(event)
29163     {
29164         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29165     },
29166     
29167     isApplied: function() 
29168     {
29169         return this.getGmapContext() == false ? false : true;
29170     },
29171     
29172     getGmapContext: function() 
29173     {
29174         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29175     },
29176     
29177     GMapContext: function() 
29178     {
29179         var position = new google.maps.LatLng(this.latitude, this.longitude);
29180         
29181         var _map = new google.maps.Map(this.el.dom, {
29182             center: position,
29183             zoom: this.zoom,
29184             mapTypeId: this.mapTypeId,
29185             mapTypeControl: this.mapTypeControl,
29186             disableDoubleClickZoom: this.disableDoubleClickZoom,
29187             scrollwheel: this.scrollwheel,
29188             streetViewControl: this.streetViewControl,
29189             locationName: this.locationName,
29190             draggable: this.draggable,
29191             enableAutocomplete: this.enableAutocomplete,
29192             enableReverseGeocode: this.enableReverseGeocode
29193         });
29194         
29195         var _marker = new google.maps.Marker({
29196             position: position,
29197             map: _map,
29198             title: this.markerTitle,
29199             draggable: this.draggable
29200         });
29201         
29202         return {
29203             map: _map,
29204             marker: _marker,
29205             circle: null,
29206             location: position,
29207             radius: this.radius,
29208             locationName: this.locationName,
29209             addressComponents: {
29210                 formatted_address: null,
29211                 addressLine1: null,
29212                 addressLine2: null,
29213                 streetName: null,
29214                 streetNumber: null,
29215                 city: null,
29216                 district: null,
29217                 state: null,
29218                 stateOrProvince: null
29219             },
29220             settings: this,
29221             domContainer: this.el.dom,
29222             geodecoder: new google.maps.Geocoder()
29223         };
29224     },
29225     
29226     drawCircle: function(center, radius, options) 
29227     {
29228         if (this.gMapContext.circle != null) {
29229             this.gMapContext.circle.setMap(null);
29230         }
29231         if (radius > 0) {
29232             radius *= 1;
29233             options = Roo.apply({}, options, {
29234                 strokeColor: "#0000FF",
29235                 strokeOpacity: .35,
29236                 strokeWeight: 2,
29237                 fillColor: "#0000FF",
29238                 fillOpacity: .2
29239             });
29240             
29241             options.map = this.gMapContext.map;
29242             options.radius = radius;
29243             options.center = center;
29244             this.gMapContext.circle = new google.maps.Circle(options);
29245             return this.gMapContext.circle;
29246         }
29247         
29248         return null;
29249     },
29250     
29251     setPosition: function(location) 
29252     {
29253         this.gMapContext.location = location;
29254         this.gMapContext.marker.setPosition(location);
29255         this.gMapContext.map.panTo(location);
29256         this.drawCircle(location, this.gMapContext.radius, {});
29257         
29258         var _this = this;
29259         
29260         if (this.gMapContext.settings.enableReverseGeocode) {
29261             this.gMapContext.geodecoder.geocode({
29262                 latLng: this.gMapContext.location
29263             }, function(results, status) {
29264                 
29265                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29266                     _this.gMapContext.locationName = results[0].formatted_address;
29267                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29268                     
29269                     _this.fireEvent('positionchanged', this, location);
29270                 }
29271             });
29272             
29273             return;
29274         }
29275         
29276         this.fireEvent('positionchanged', this, location);
29277     },
29278     
29279     resize: function()
29280     {
29281         google.maps.event.trigger(this.gMapContext.map, "resize");
29282         
29283         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29284         
29285         this.fireEvent('resize', this);
29286     },
29287     
29288     setPositionByLatLng: function(latitude, longitude)
29289     {
29290         this.setPosition(new google.maps.LatLng(latitude, longitude));
29291     },
29292     
29293     getCurrentPosition: function() 
29294     {
29295         return {
29296             latitude: this.gMapContext.location.lat(),
29297             longitude: this.gMapContext.location.lng()
29298         };
29299     },
29300     
29301     getAddressName: function() 
29302     {
29303         return this.gMapContext.locationName;
29304     },
29305     
29306     getAddressComponents: function() 
29307     {
29308         return this.gMapContext.addressComponents;
29309     },
29310     
29311     address_component_from_google_geocode: function(address_components) 
29312     {
29313         var result = {};
29314         
29315         for (var i = 0; i < address_components.length; i++) {
29316             var component = address_components[i];
29317             if (component.types.indexOf("postal_code") >= 0) {
29318                 result.postalCode = component.short_name;
29319             } else if (component.types.indexOf("street_number") >= 0) {
29320                 result.streetNumber = component.short_name;
29321             } else if (component.types.indexOf("route") >= 0) {
29322                 result.streetName = component.short_name;
29323             } else if (component.types.indexOf("neighborhood") >= 0) {
29324                 result.city = component.short_name;
29325             } else if (component.types.indexOf("locality") >= 0) {
29326                 result.city = component.short_name;
29327             } else if (component.types.indexOf("sublocality") >= 0) {
29328                 result.district = component.short_name;
29329             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29330                 result.stateOrProvince = component.short_name;
29331             } else if (component.types.indexOf("country") >= 0) {
29332                 result.country = component.short_name;
29333             }
29334         }
29335         
29336         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29337         result.addressLine2 = "";
29338         return result;
29339     },
29340     
29341     setZoomLevel: function(zoom)
29342     {
29343         this.gMapContext.map.setZoom(zoom);
29344     },
29345     
29346     show: function()
29347     {
29348         if(!this.el){
29349             return;
29350         }
29351         
29352         this.el.show();
29353         
29354         this.resize();
29355         
29356         this.fireEvent('show', this);
29357     },
29358     
29359     hide: function()
29360     {
29361         if(!this.el){
29362             return;
29363         }
29364         
29365         this.el.hide();
29366         
29367         this.fireEvent('hide', this);
29368     }
29369     
29370 });
29371
29372 Roo.apply(Roo.bootstrap.LocationPicker, {
29373     
29374     OverlayView : function(map, options)
29375     {
29376         options = options || {};
29377         
29378         this.setMap(map);
29379     }
29380     
29381     
29382 });/**
29383  * @class Roo.bootstrap.Alert
29384  * @extends Roo.bootstrap.Component
29385  * Bootstrap Alert class - shows an alert area box
29386  * eg
29387  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29388   Enter a valid email address
29389 </div>
29390  * @licence LGPL
29391  * @cfg {String} title The title of alert
29392  * @cfg {String} html The content of alert
29393  * @cfg {String} weight (  success | info | warning | danger )
29394  * @cfg {String} faicon font-awesomeicon
29395  * 
29396  * @constructor
29397  * Create a new alert
29398  * @param {Object} config The config object
29399  */
29400
29401
29402 Roo.bootstrap.Alert = function(config){
29403     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29404     
29405 };
29406
29407 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29408     
29409     title: '',
29410     html: '',
29411     weight: false,
29412     faicon: false,
29413     
29414     getAutoCreate : function()
29415     {
29416         
29417         var cfg = {
29418             tag : 'div',
29419             cls : 'alert',
29420             cn : [
29421                 {
29422                     tag : 'i',
29423                     cls : 'roo-alert-icon'
29424                     
29425                 },
29426                 {
29427                     tag : 'b',
29428                     cls : 'roo-alert-title',
29429                     html : this.title
29430                 },
29431                 {
29432                     tag : 'span',
29433                     cls : 'roo-alert-text',
29434                     html : this.html
29435                 }
29436             ]
29437         };
29438         
29439         if(this.faicon){
29440             cfg.cn[0].cls += ' fa ' + this.faicon;
29441         }
29442         
29443         if(this.weight){
29444             cfg.cls += ' alert-' + this.weight;
29445         }
29446         
29447         return cfg;
29448     },
29449     
29450     initEvents: function() 
29451     {
29452         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29453     },
29454     
29455     setTitle : function(str)
29456     {
29457         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29458     },
29459     
29460     setText : function(str)
29461     {
29462         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29463     },
29464     
29465     setWeight : function(weight)
29466     {
29467         if(this.weight){
29468             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29469         }
29470         
29471         this.weight = weight;
29472         
29473         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29474     },
29475     
29476     setIcon : function(icon)
29477     {
29478         if(this.faicon){
29479             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29480         }
29481         
29482         this.faicon = icon;
29483         
29484         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29485     },
29486     
29487     hide: function() 
29488     {
29489         this.el.hide();   
29490     },
29491     
29492     show: function() 
29493     {  
29494         this.el.show();   
29495     }
29496     
29497 });
29498
29499  
29500 /*
29501 * Licence: LGPL
29502 */
29503
29504 /**
29505  * @class Roo.bootstrap.UploadCropbox
29506  * @extends Roo.bootstrap.Component
29507  * Bootstrap UploadCropbox class
29508  * @cfg {String} emptyText show when image has been loaded
29509  * @cfg {String} rotateNotify show when image too small to rotate
29510  * @cfg {Number} errorTimeout default 3000
29511  * @cfg {Number} minWidth default 300
29512  * @cfg {Number} minHeight default 300
29513  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29514  * @cfg {Boolean} isDocument (true|false) default false
29515  * @cfg {String} url action url
29516  * @cfg {String} paramName default 'imageUpload'
29517  * @cfg {String} method default POST
29518  * @cfg {Boolean} loadMask (true|false) default true
29519  * @cfg {Boolean} loadingText default 'Loading...'
29520  * 
29521  * @constructor
29522  * Create a new UploadCropbox
29523  * @param {Object} config The config object
29524  */
29525
29526 Roo.bootstrap.UploadCropbox = function(config){
29527     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29528     
29529     this.addEvents({
29530         /**
29531          * @event beforeselectfile
29532          * Fire before select file
29533          * @param {Roo.bootstrap.UploadCropbox} this
29534          */
29535         "beforeselectfile" : true,
29536         /**
29537          * @event initial
29538          * Fire after initEvent
29539          * @param {Roo.bootstrap.UploadCropbox} this
29540          */
29541         "initial" : true,
29542         /**
29543          * @event crop
29544          * Fire after initEvent
29545          * @param {Roo.bootstrap.UploadCropbox} this
29546          * @param {String} data
29547          */
29548         "crop" : true,
29549         /**
29550          * @event prepare
29551          * Fire when preparing the file data
29552          * @param {Roo.bootstrap.UploadCropbox} this
29553          * @param {Object} file
29554          */
29555         "prepare" : true,
29556         /**
29557          * @event exception
29558          * Fire when get exception
29559          * @param {Roo.bootstrap.UploadCropbox} this
29560          * @param {XMLHttpRequest} xhr
29561          */
29562         "exception" : true,
29563         /**
29564          * @event beforeloadcanvas
29565          * Fire before load the canvas
29566          * @param {Roo.bootstrap.UploadCropbox} this
29567          * @param {String} src
29568          */
29569         "beforeloadcanvas" : true,
29570         /**
29571          * @event trash
29572          * Fire when trash image
29573          * @param {Roo.bootstrap.UploadCropbox} this
29574          */
29575         "trash" : true,
29576         /**
29577          * @event download
29578          * Fire when download the image
29579          * @param {Roo.bootstrap.UploadCropbox} this
29580          */
29581         "download" : true,
29582         /**
29583          * @event footerbuttonclick
29584          * Fire when footerbuttonclick
29585          * @param {Roo.bootstrap.UploadCropbox} this
29586          * @param {String} type
29587          */
29588         "footerbuttonclick" : true,
29589         /**
29590          * @event resize
29591          * Fire when resize
29592          * @param {Roo.bootstrap.UploadCropbox} this
29593          */
29594         "resize" : true,
29595         /**
29596          * @event rotate
29597          * Fire when rotate the image
29598          * @param {Roo.bootstrap.UploadCropbox} this
29599          * @param {String} pos
29600          */
29601         "rotate" : true,
29602         /**
29603          * @event inspect
29604          * Fire when inspect the file
29605          * @param {Roo.bootstrap.UploadCropbox} this
29606          * @param {Object} file
29607          */
29608         "inspect" : true,
29609         /**
29610          * @event upload
29611          * Fire when xhr upload the file
29612          * @param {Roo.bootstrap.UploadCropbox} this
29613          * @param {Object} data
29614          */
29615         "upload" : true,
29616         /**
29617          * @event arrange
29618          * Fire when arrange the file data
29619          * @param {Roo.bootstrap.UploadCropbox} this
29620          * @param {Object} formData
29621          */
29622         "arrange" : true
29623     });
29624     
29625     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29626 };
29627
29628 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29629     
29630     emptyText : 'Click to upload image',
29631     rotateNotify : 'Image is too small to rotate',
29632     errorTimeout : 3000,
29633     scale : 0,
29634     baseScale : 1,
29635     rotate : 0,
29636     dragable : false,
29637     pinching : false,
29638     mouseX : 0,
29639     mouseY : 0,
29640     cropData : false,
29641     minWidth : 300,
29642     minHeight : 300,
29643     file : false,
29644     exif : {},
29645     baseRotate : 1,
29646     cropType : 'image/jpeg',
29647     buttons : false,
29648     canvasLoaded : false,
29649     isDocument : false,
29650     method : 'POST',
29651     paramName : 'imageUpload',
29652     loadMask : true,
29653     loadingText : 'Loading...',
29654     maskEl : false,
29655     
29656     getAutoCreate : function()
29657     {
29658         var cfg = {
29659             tag : 'div',
29660             cls : 'roo-upload-cropbox',
29661             cn : [
29662                 {
29663                     tag : 'input',
29664                     cls : 'roo-upload-cropbox-selector',
29665                     type : 'file'
29666                 },
29667                 {
29668                     tag : 'div',
29669                     cls : 'roo-upload-cropbox-body',
29670                     style : 'cursor:pointer',
29671                     cn : [
29672                         {
29673                             tag : 'div',
29674                             cls : 'roo-upload-cropbox-preview'
29675                         },
29676                         {
29677                             tag : 'div',
29678                             cls : 'roo-upload-cropbox-thumb'
29679                         },
29680                         {
29681                             tag : 'div',
29682                             cls : 'roo-upload-cropbox-empty-notify',
29683                             html : this.emptyText
29684                         },
29685                         {
29686                             tag : 'div',
29687                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29688                             html : this.rotateNotify
29689                         }
29690                     ]
29691                 },
29692                 {
29693                     tag : 'div',
29694                     cls : 'roo-upload-cropbox-footer',
29695                     cn : {
29696                         tag : 'div',
29697                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29698                         cn : []
29699                     }
29700                 }
29701             ]
29702         };
29703         
29704         return cfg;
29705     },
29706     
29707     onRender : function(ct, position)
29708     {
29709         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29710         
29711         if (this.buttons.length) {
29712             
29713             Roo.each(this.buttons, function(bb) {
29714                 
29715                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29716                 
29717                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29718                 
29719             }, this);
29720         }
29721         
29722         if(this.loadMask){
29723             this.maskEl = this.el;
29724         }
29725     },
29726     
29727     initEvents : function()
29728     {
29729         this.urlAPI = (window.createObjectURL && window) || 
29730                                 (window.URL && URL.revokeObjectURL && URL) || 
29731                                 (window.webkitURL && webkitURL);
29732                         
29733         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29734         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29735         
29736         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29737         this.selectorEl.hide();
29738         
29739         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29740         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29741         
29742         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29743         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29744         this.thumbEl.hide();
29745         
29746         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29747         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29748         
29749         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29750         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29751         this.errorEl.hide();
29752         
29753         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29754         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29755         this.footerEl.hide();
29756         
29757         this.setThumbBoxSize();
29758         
29759         this.bind();
29760         
29761         this.resize();
29762         
29763         this.fireEvent('initial', this);
29764     },
29765
29766     bind : function()
29767     {
29768         var _this = this;
29769         
29770         window.addEventListener("resize", function() { _this.resize(); } );
29771         
29772         this.bodyEl.on('click', this.beforeSelectFile, this);
29773         
29774         if(Roo.isTouch){
29775             this.bodyEl.on('touchstart', this.onTouchStart, this);
29776             this.bodyEl.on('touchmove', this.onTouchMove, this);
29777             this.bodyEl.on('touchend', this.onTouchEnd, this);
29778         }
29779         
29780         if(!Roo.isTouch){
29781             this.bodyEl.on('mousedown', this.onMouseDown, this);
29782             this.bodyEl.on('mousemove', this.onMouseMove, this);
29783             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29784             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29785             Roo.get(document).on('mouseup', this.onMouseUp, this);
29786         }
29787         
29788         this.selectorEl.on('change', this.onFileSelected, this);
29789     },
29790     
29791     reset : function()
29792     {    
29793         this.scale = 0;
29794         this.baseScale = 1;
29795         this.rotate = 0;
29796         this.baseRotate = 1;
29797         this.dragable = false;
29798         this.pinching = false;
29799         this.mouseX = 0;
29800         this.mouseY = 0;
29801         this.cropData = false;
29802         this.notifyEl.dom.innerHTML = this.emptyText;
29803         
29804         this.selectorEl.dom.value = '';
29805         
29806     },
29807     
29808     resize : function()
29809     {
29810         if(this.fireEvent('resize', this) != false){
29811             this.setThumbBoxPosition();
29812             this.setCanvasPosition();
29813         }
29814     },
29815     
29816     onFooterButtonClick : function(e, el, o, type)
29817     {
29818         switch (type) {
29819             case 'rotate-left' :
29820                 this.onRotateLeft(e);
29821                 break;
29822             case 'rotate-right' :
29823                 this.onRotateRight(e);
29824                 break;
29825             case 'picture' :
29826                 this.beforeSelectFile(e);
29827                 break;
29828             case 'trash' :
29829                 this.trash(e);
29830                 break;
29831             case 'crop' :
29832                 this.crop(e);
29833                 break;
29834             case 'download' :
29835                 this.download(e);
29836                 break;
29837             default :
29838                 break;
29839         }
29840         
29841         this.fireEvent('footerbuttonclick', this, type);
29842     },
29843     
29844     beforeSelectFile : function(e)
29845     {
29846         e.preventDefault();
29847         
29848         if(this.fireEvent('beforeselectfile', this) != false){
29849             this.selectorEl.dom.click();
29850         }
29851     },
29852     
29853     onFileSelected : function(e)
29854     {
29855         e.preventDefault();
29856         
29857         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29858             return;
29859         }
29860         
29861         var file = this.selectorEl.dom.files[0];
29862         
29863         if(this.fireEvent('inspect', this, file) != false){
29864             this.prepare(file);
29865         }
29866         
29867     },
29868     
29869     trash : function(e)
29870     {
29871         this.fireEvent('trash', this);
29872     },
29873     
29874     download : function(e)
29875     {
29876         this.fireEvent('download', this);
29877     },
29878     
29879     loadCanvas : function(src)
29880     {   
29881         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29882             
29883             this.reset();
29884             
29885             this.imageEl = document.createElement('img');
29886             
29887             var _this = this;
29888             
29889             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29890             
29891             this.imageEl.src = src;
29892         }
29893     },
29894     
29895     onLoadCanvas : function()
29896     {   
29897         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29898         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29899         
29900         this.bodyEl.un('click', this.beforeSelectFile, this);
29901         
29902         this.notifyEl.hide();
29903         this.thumbEl.show();
29904         this.footerEl.show();
29905         
29906         this.baseRotateLevel();
29907         
29908         if(this.isDocument){
29909             this.setThumbBoxSize();
29910         }
29911         
29912         this.setThumbBoxPosition();
29913         
29914         this.baseScaleLevel();
29915         
29916         this.draw();
29917         
29918         this.resize();
29919         
29920         this.canvasLoaded = true;
29921         
29922         if(this.loadMask){
29923             this.maskEl.unmask();
29924         }
29925         
29926     },
29927     
29928     setCanvasPosition : function()
29929     {   
29930         if(!this.canvasEl){
29931             return;
29932         }
29933         
29934         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29935         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29936         
29937         this.previewEl.setLeft(pw);
29938         this.previewEl.setTop(ph);
29939         
29940     },
29941     
29942     onMouseDown : function(e)
29943     {   
29944         e.stopEvent();
29945         
29946         this.dragable = true;
29947         this.pinching = false;
29948         
29949         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29950             this.dragable = false;
29951             return;
29952         }
29953         
29954         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29955         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29956         
29957     },
29958     
29959     onMouseMove : function(e)
29960     {   
29961         e.stopEvent();
29962         
29963         if(!this.canvasLoaded){
29964             return;
29965         }
29966         
29967         if (!this.dragable){
29968             return;
29969         }
29970         
29971         var minX = Math.ceil(this.thumbEl.getLeft(true));
29972         var minY = Math.ceil(this.thumbEl.getTop(true));
29973         
29974         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29975         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29976         
29977         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29978         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29979         
29980         x = x - this.mouseX;
29981         y = y - this.mouseY;
29982         
29983         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29984         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29985         
29986         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29987         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29988         
29989         this.previewEl.setLeft(bgX);
29990         this.previewEl.setTop(bgY);
29991         
29992         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29993         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29994     },
29995     
29996     onMouseUp : function(e)
29997     {   
29998         e.stopEvent();
29999         
30000         this.dragable = false;
30001     },
30002     
30003     onMouseWheel : function(e)
30004     {   
30005         e.stopEvent();
30006         
30007         this.startScale = this.scale;
30008         
30009         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30010         
30011         if(!this.zoomable()){
30012             this.scale = this.startScale;
30013             return;
30014         }
30015         
30016         this.draw();
30017         
30018         return;
30019     },
30020     
30021     zoomable : function()
30022     {
30023         var minScale = this.thumbEl.getWidth() / this.minWidth;
30024         
30025         if(this.minWidth < this.minHeight){
30026             minScale = this.thumbEl.getHeight() / this.minHeight;
30027         }
30028         
30029         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30030         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30031         
30032         if(
30033                 this.isDocument &&
30034                 (this.rotate == 0 || this.rotate == 180) && 
30035                 (
30036                     width > this.imageEl.OriginWidth || 
30037                     height > this.imageEl.OriginHeight ||
30038                     (width < this.minWidth && height < this.minHeight)
30039                 )
30040         ){
30041             return false;
30042         }
30043         
30044         if(
30045                 this.isDocument &&
30046                 (this.rotate == 90 || this.rotate == 270) && 
30047                 (
30048                     width > this.imageEl.OriginWidth || 
30049                     height > this.imageEl.OriginHeight ||
30050                     (width < this.minHeight && height < this.minWidth)
30051                 )
30052         ){
30053             return false;
30054         }
30055         
30056         if(
30057                 !this.isDocument &&
30058                 (this.rotate == 0 || this.rotate == 180) && 
30059                 (
30060                     width < this.minWidth || 
30061                     width > this.imageEl.OriginWidth || 
30062                     height < this.minHeight || 
30063                     height > this.imageEl.OriginHeight
30064                 )
30065         ){
30066             return false;
30067         }
30068         
30069         if(
30070                 !this.isDocument &&
30071                 (this.rotate == 90 || this.rotate == 270) && 
30072                 (
30073                     width < this.minHeight || 
30074                     width > this.imageEl.OriginWidth || 
30075                     height < this.minWidth || 
30076                     height > this.imageEl.OriginHeight
30077                 )
30078         ){
30079             return false;
30080         }
30081         
30082         return true;
30083         
30084     },
30085     
30086     onRotateLeft : function(e)
30087     {   
30088         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30089             
30090             var minScale = this.thumbEl.getWidth() / this.minWidth;
30091             
30092             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30093             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30094             
30095             this.startScale = this.scale;
30096             
30097             while (this.getScaleLevel() < minScale){
30098             
30099                 this.scale = this.scale + 1;
30100                 
30101                 if(!this.zoomable()){
30102                     break;
30103                 }
30104                 
30105                 if(
30106                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30107                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30108                 ){
30109                     continue;
30110                 }
30111                 
30112                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30113
30114                 this.draw();
30115                 
30116                 return;
30117             }
30118             
30119             this.scale = this.startScale;
30120             
30121             this.onRotateFail();
30122             
30123             return false;
30124         }
30125         
30126         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30127
30128         if(this.isDocument){
30129             this.setThumbBoxSize();
30130             this.setThumbBoxPosition();
30131             this.setCanvasPosition();
30132         }
30133         
30134         this.draw();
30135         
30136         this.fireEvent('rotate', this, 'left');
30137         
30138     },
30139     
30140     onRotateRight : function(e)
30141     {
30142         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30143             
30144             var minScale = this.thumbEl.getWidth() / this.minWidth;
30145         
30146             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30147             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30148             
30149             this.startScale = this.scale;
30150             
30151             while (this.getScaleLevel() < minScale){
30152             
30153                 this.scale = this.scale + 1;
30154                 
30155                 if(!this.zoomable()){
30156                     break;
30157                 }
30158                 
30159                 if(
30160                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30161                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30162                 ){
30163                     continue;
30164                 }
30165                 
30166                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30167
30168                 this.draw();
30169                 
30170                 return;
30171             }
30172             
30173             this.scale = this.startScale;
30174             
30175             this.onRotateFail();
30176             
30177             return false;
30178         }
30179         
30180         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30181
30182         if(this.isDocument){
30183             this.setThumbBoxSize();
30184             this.setThumbBoxPosition();
30185             this.setCanvasPosition();
30186         }
30187         
30188         this.draw();
30189         
30190         this.fireEvent('rotate', this, 'right');
30191     },
30192     
30193     onRotateFail : function()
30194     {
30195         this.errorEl.show(true);
30196         
30197         var _this = this;
30198         
30199         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30200     },
30201     
30202     draw : function()
30203     {
30204         this.previewEl.dom.innerHTML = '';
30205         
30206         var canvasEl = document.createElement("canvas");
30207         
30208         var contextEl = canvasEl.getContext("2d");
30209         
30210         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30211         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30212         var center = this.imageEl.OriginWidth / 2;
30213         
30214         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30215             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30216             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30217             center = this.imageEl.OriginHeight / 2;
30218         }
30219         
30220         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30221         
30222         contextEl.translate(center, center);
30223         contextEl.rotate(this.rotate * Math.PI / 180);
30224
30225         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30226         
30227         this.canvasEl = document.createElement("canvas");
30228         
30229         this.contextEl = this.canvasEl.getContext("2d");
30230         
30231         switch (this.rotate) {
30232             case 0 :
30233                 
30234                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30235                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30236                 
30237                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30238                 
30239                 break;
30240             case 90 : 
30241                 
30242                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30243                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30244                 
30245                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30246                     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);
30247                     break;
30248                 }
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 180 :
30254                 
30255                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30256                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30257                 
30258                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30259                     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);
30260                     break;
30261                 }
30262                 
30263                 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);
30264                 
30265                 break;
30266             case 270 :
30267                 
30268                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30269                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30270         
30271                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30272                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30273                     break;
30274                 }
30275                 
30276                 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);
30277                 
30278                 break;
30279             default : 
30280                 break;
30281         }
30282         
30283         this.previewEl.appendChild(this.canvasEl);
30284         
30285         this.setCanvasPosition();
30286     },
30287     
30288     crop : function()
30289     {
30290         if(!this.canvasLoaded){
30291             return;
30292         }
30293         
30294         var imageCanvas = document.createElement("canvas");
30295         
30296         var imageContext = imageCanvas.getContext("2d");
30297         
30298         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30299         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30300         
30301         var center = imageCanvas.width / 2;
30302         
30303         imageContext.translate(center, center);
30304         
30305         imageContext.rotate(this.rotate * Math.PI / 180);
30306         
30307         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30308         
30309         var canvas = document.createElement("canvas");
30310         
30311         var context = canvas.getContext("2d");
30312                 
30313         canvas.width = this.minWidth;
30314         canvas.height = this.minHeight;
30315
30316         switch (this.rotate) {
30317             case 0 :
30318                 
30319                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30320                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30321                 
30322                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30323                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30324                 
30325                 var targetWidth = this.minWidth - 2 * x;
30326                 var targetHeight = this.minHeight - 2 * y;
30327                 
30328                 var scale = 1;
30329                 
30330                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30331                     scale = targetWidth / width;
30332                 }
30333                 
30334                 if(x > 0 && y == 0){
30335                     scale = targetHeight / height;
30336                 }
30337                 
30338                 if(x > 0 && y > 0){
30339                     scale = targetWidth / width;
30340                     
30341                     if(width < height){
30342                         scale = targetHeight / height;
30343                     }
30344                 }
30345                 
30346                 context.scale(scale, scale);
30347                 
30348                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30349                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30350
30351                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30352                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30353
30354                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30355                 
30356                 break;
30357             case 90 : 
30358                 
30359                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30360                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30361                 
30362                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30363                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30364                 
30365                 var targetWidth = this.minWidth - 2 * x;
30366                 var targetHeight = this.minHeight - 2 * y;
30367                 
30368                 var scale = 1;
30369                 
30370                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30371                     scale = targetWidth / width;
30372                 }
30373                 
30374                 if(x > 0 && y == 0){
30375                     scale = targetHeight / height;
30376                 }
30377                 
30378                 if(x > 0 && y > 0){
30379                     scale = targetWidth / width;
30380                     
30381                     if(width < height){
30382                         scale = targetHeight / height;
30383                     }
30384                 }
30385                 
30386                 context.scale(scale, scale);
30387                 
30388                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30389                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30390
30391                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30392                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30393                 
30394                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30395                 
30396                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30397                 
30398                 break;
30399             case 180 :
30400                 
30401                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30402                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30403                 
30404                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30405                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30406                 
30407                 var targetWidth = this.minWidth - 2 * x;
30408                 var targetHeight = this.minHeight - 2 * y;
30409                 
30410                 var scale = 1;
30411                 
30412                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30413                     scale = targetWidth / width;
30414                 }
30415                 
30416                 if(x > 0 && y == 0){
30417                     scale = targetHeight / height;
30418                 }
30419                 
30420                 if(x > 0 && y > 0){
30421                     scale = targetWidth / width;
30422                     
30423                     if(width < height){
30424                         scale = targetHeight / height;
30425                     }
30426                 }
30427                 
30428                 context.scale(scale, scale);
30429                 
30430                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30431                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30432
30433                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30434                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30435
30436                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30437                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30438                 
30439                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30440                 
30441                 break;
30442             case 270 :
30443                 
30444                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30445                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30446                 
30447                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30448                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30449                 
30450                 var targetWidth = this.minWidth - 2 * x;
30451                 var targetHeight = this.minHeight - 2 * y;
30452                 
30453                 var scale = 1;
30454                 
30455                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30456                     scale = targetWidth / width;
30457                 }
30458                 
30459                 if(x > 0 && y == 0){
30460                     scale = targetHeight / height;
30461                 }
30462                 
30463                 if(x > 0 && y > 0){
30464                     scale = targetWidth / width;
30465                     
30466                     if(width < height){
30467                         scale = targetHeight / height;
30468                     }
30469                 }
30470                 
30471                 context.scale(scale, scale);
30472                 
30473                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30474                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30475
30476                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30477                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30478                 
30479                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30480                 
30481                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30482                 
30483                 break;
30484             default : 
30485                 break;
30486         }
30487         
30488         this.cropData = canvas.toDataURL(this.cropType);
30489         
30490         if(this.fireEvent('crop', this, this.cropData) !== false){
30491             this.process(this.file, this.cropData);
30492         }
30493         
30494         return;
30495         
30496     },
30497     
30498     setThumbBoxSize : function()
30499     {
30500         var width, height;
30501         
30502         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30503             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30504             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30505             
30506             this.minWidth = width;
30507             this.minHeight = height;
30508             
30509             if(this.rotate == 90 || this.rotate == 270){
30510                 this.minWidth = height;
30511                 this.minHeight = width;
30512             }
30513         }
30514         
30515         height = 300;
30516         width = Math.ceil(this.minWidth * height / this.minHeight);
30517         
30518         if(this.minWidth > this.minHeight){
30519             width = 300;
30520             height = Math.ceil(this.minHeight * width / this.minWidth);
30521         }
30522         
30523         this.thumbEl.setStyle({
30524             width : width + 'px',
30525             height : height + 'px'
30526         });
30527
30528         return;
30529             
30530     },
30531     
30532     setThumbBoxPosition : function()
30533     {
30534         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30535         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30536         
30537         this.thumbEl.setLeft(x);
30538         this.thumbEl.setTop(y);
30539         
30540     },
30541     
30542     baseRotateLevel : function()
30543     {
30544         this.baseRotate = 1;
30545         
30546         if(
30547                 typeof(this.exif) != 'undefined' &&
30548                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30549                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30550         ){
30551             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30552         }
30553         
30554         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30555         
30556     },
30557     
30558     baseScaleLevel : function()
30559     {
30560         var width, height;
30561         
30562         if(this.isDocument){
30563             
30564             if(this.baseRotate == 6 || this.baseRotate == 8){
30565             
30566                 height = this.thumbEl.getHeight();
30567                 this.baseScale = height / this.imageEl.OriginWidth;
30568
30569                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30570                     width = this.thumbEl.getWidth();
30571                     this.baseScale = width / this.imageEl.OriginHeight;
30572                 }
30573
30574                 return;
30575             }
30576
30577             height = this.thumbEl.getHeight();
30578             this.baseScale = height / this.imageEl.OriginHeight;
30579
30580             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30581                 width = this.thumbEl.getWidth();
30582                 this.baseScale = width / this.imageEl.OriginWidth;
30583             }
30584
30585             return;
30586         }
30587         
30588         if(this.baseRotate == 6 || this.baseRotate == 8){
30589             
30590             width = this.thumbEl.getHeight();
30591             this.baseScale = width / this.imageEl.OriginHeight;
30592             
30593             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30594                 height = this.thumbEl.getWidth();
30595                 this.baseScale = height / this.imageEl.OriginHeight;
30596             }
30597             
30598             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30599                 height = this.thumbEl.getWidth();
30600                 this.baseScale = height / this.imageEl.OriginHeight;
30601                 
30602                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30603                     width = this.thumbEl.getHeight();
30604                     this.baseScale = width / this.imageEl.OriginWidth;
30605                 }
30606             }
30607             
30608             return;
30609         }
30610         
30611         width = this.thumbEl.getWidth();
30612         this.baseScale = width / this.imageEl.OriginWidth;
30613         
30614         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30615             height = this.thumbEl.getHeight();
30616             this.baseScale = height / this.imageEl.OriginHeight;
30617         }
30618         
30619         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30620             
30621             height = this.thumbEl.getHeight();
30622             this.baseScale = height / this.imageEl.OriginHeight;
30623             
30624             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30625                 width = this.thumbEl.getWidth();
30626                 this.baseScale = width / this.imageEl.OriginWidth;
30627             }
30628             
30629         }
30630         
30631         return;
30632     },
30633     
30634     getScaleLevel : function()
30635     {
30636         return this.baseScale * Math.pow(1.1, this.scale);
30637     },
30638     
30639     onTouchStart : function(e)
30640     {
30641         if(!this.canvasLoaded){
30642             this.beforeSelectFile(e);
30643             return;
30644         }
30645         
30646         var touches = e.browserEvent.touches;
30647         
30648         if(!touches){
30649             return;
30650         }
30651         
30652         if(touches.length == 1){
30653             this.onMouseDown(e);
30654             return;
30655         }
30656         
30657         if(touches.length != 2){
30658             return;
30659         }
30660         
30661         var coords = [];
30662         
30663         for(var i = 0, finger; finger = touches[i]; i++){
30664             coords.push(finger.pageX, finger.pageY);
30665         }
30666         
30667         var x = Math.pow(coords[0] - coords[2], 2);
30668         var y = Math.pow(coords[1] - coords[3], 2);
30669         
30670         this.startDistance = Math.sqrt(x + y);
30671         
30672         this.startScale = this.scale;
30673         
30674         this.pinching = true;
30675         this.dragable = false;
30676         
30677     },
30678     
30679     onTouchMove : function(e)
30680     {
30681         if(!this.pinching && !this.dragable){
30682             return;
30683         }
30684         
30685         var touches = e.browserEvent.touches;
30686         
30687         if(!touches){
30688             return;
30689         }
30690         
30691         if(this.dragable){
30692             this.onMouseMove(e);
30693             return;
30694         }
30695         
30696         var coords = [];
30697         
30698         for(var i = 0, finger; finger = touches[i]; i++){
30699             coords.push(finger.pageX, finger.pageY);
30700         }
30701         
30702         var x = Math.pow(coords[0] - coords[2], 2);
30703         var y = Math.pow(coords[1] - coords[3], 2);
30704         
30705         this.endDistance = Math.sqrt(x + y);
30706         
30707         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30708         
30709         if(!this.zoomable()){
30710             this.scale = this.startScale;
30711             return;
30712         }
30713         
30714         this.draw();
30715         
30716     },
30717     
30718     onTouchEnd : function(e)
30719     {
30720         this.pinching = false;
30721         this.dragable = false;
30722         
30723     },
30724     
30725     process : function(file, crop)
30726     {
30727         if(this.loadMask){
30728             this.maskEl.mask(this.loadingText);
30729         }
30730         
30731         this.xhr = new XMLHttpRequest();
30732         
30733         file.xhr = this.xhr;
30734
30735         this.xhr.open(this.method, this.url, true);
30736         
30737         var headers = {
30738             "Accept": "application/json",
30739             "Cache-Control": "no-cache",
30740             "X-Requested-With": "XMLHttpRequest"
30741         };
30742         
30743         for (var headerName in headers) {
30744             var headerValue = headers[headerName];
30745             if (headerValue) {
30746                 this.xhr.setRequestHeader(headerName, headerValue);
30747             }
30748         }
30749         
30750         var _this = this;
30751         
30752         this.xhr.onload = function()
30753         {
30754             _this.xhrOnLoad(_this.xhr);
30755         }
30756         
30757         this.xhr.onerror = function()
30758         {
30759             _this.xhrOnError(_this.xhr);
30760         }
30761         
30762         var formData = new FormData();
30763
30764         formData.append('returnHTML', 'NO');
30765         
30766         if(crop){
30767             formData.append('crop', crop);
30768         }
30769         
30770         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30771             formData.append(this.paramName, file, file.name);
30772         }
30773         
30774         if(typeof(file.filename) != 'undefined'){
30775             formData.append('filename', file.filename);
30776         }
30777         
30778         if(typeof(file.mimetype) != 'undefined'){
30779             formData.append('mimetype', file.mimetype);
30780         }
30781         
30782         if(this.fireEvent('arrange', this, formData) != false){
30783             this.xhr.send(formData);
30784         };
30785     },
30786     
30787     xhrOnLoad : function(xhr)
30788     {
30789         if(this.loadMask){
30790             this.maskEl.unmask();
30791         }
30792         
30793         if (xhr.readyState !== 4) {
30794             this.fireEvent('exception', this, xhr);
30795             return;
30796         }
30797
30798         var response = Roo.decode(xhr.responseText);
30799         
30800         if(!response.success){
30801             this.fireEvent('exception', this, xhr);
30802             return;
30803         }
30804         
30805         var response = Roo.decode(xhr.responseText);
30806         
30807         this.fireEvent('upload', this, response);
30808         
30809     },
30810     
30811     xhrOnError : function()
30812     {
30813         if(this.loadMask){
30814             this.maskEl.unmask();
30815         }
30816         
30817         Roo.log('xhr on error');
30818         
30819         var response = Roo.decode(xhr.responseText);
30820           
30821         Roo.log(response);
30822         
30823     },
30824     
30825     prepare : function(file)
30826     {   
30827         if(this.loadMask){
30828             this.maskEl.mask(this.loadingText);
30829         }
30830         
30831         this.file = false;
30832         this.exif = {};
30833         
30834         if(typeof(file) === 'string'){
30835             this.loadCanvas(file);
30836             return;
30837         }
30838         
30839         if(!file || !this.urlAPI){
30840             return;
30841         }
30842         
30843         this.file = file;
30844         this.cropType = file.type;
30845         
30846         var _this = this;
30847         
30848         if(this.fireEvent('prepare', this, this.file) != false){
30849             
30850             var reader = new FileReader();
30851             
30852             reader.onload = function (e) {
30853                 if (e.target.error) {
30854                     Roo.log(e.target.error);
30855                     return;
30856                 }
30857                 
30858                 var buffer = e.target.result,
30859                     dataView = new DataView(buffer),
30860                     offset = 2,
30861                     maxOffset = dataView.byteLength - 4,
30862                     markerBytes,
30863                     markerLength;
30864                 
30865                 if (dataView.getUint16(0) === 0xffd8) {
30866                     while (offset < maxOffset) {
30867                         markerBytes = dataView.getUint16(offset);
30868                         
30869                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30870                             markerLength = dataView.getUint16(offset + 2) + 2;
30871                             if (offset + markerLength > dataView.byteLength) {
30872                                 Roo.log('Invalid meta data: Invalid segment size.');
30873                                 break;
30874                             }
30875                             
30876                             if(markerBytes == 0xffe1){
30877                                 _this.parseExifData(
30878                                     dataView,
30879                                     offset,
30880                                     markerLength
30881                                 );
30882                             }
30883                             
30884                             offset += markerLength;
30885                             
30886                             continue;
30887                         }
30888                         
30889                         break;
30890                     }
30891                     
30892                 }
30893                 
30894                 var url = _this.urlAPI.createObjectURL(_this.file);
30895                 
30896                 _this.loadCanvas(url);
30897                 
30898                 return;
30899             }
30900             
30901             reader.readAsArrayBuffer(this.file);
30902             
30903         }
30904         
30905     },
30906     
30907     parseExifData : function(dataView, offset, length)
30908     {
30909         var tiffOffset = offset + 10,
30910             littleEndian,
30911             dirOffset;
30912     
30913         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30914             // No Exif data, might be XMP data instead
30915             return;
30916         }
30917         
30918         // Check for the ASCII code for "Exif" (0x45786966):
30919         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30920             // No Exif data, might be XMP data instead
30921             return;
30922         }
30923         if (tiffOffset + 8 > dataView.byteLength) {
30924             Roo.log('Invalid Exif data: Invalid segment size.');
30925             return;
30926         }
30927         // Check for the two null bytes:
30928         if (dataView.getUint16(offset + 8) !== 0x0000) {
30929             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30930             return;
30931         }
30932         // Check the byte alignment:
30933         switch (dataView.getUint16(tiffOffset)) {
30934         case 0x4949:
30935             littleEndian = true;
30936             break;
30937         case 0x4D4D:
30938             littleEndian = false;
30939             break;
30940         default:
30941             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30942             return;
30943         }
30944         // Check for the TIFF tag marker (0x002A):
30945         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30946             Roo.log('Invalid Exif data: Missing TIFF marker.');
30947             return;
30948         }
30949         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30950         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30951         
30952         this.parseExifTags(
30953             dataView,
30954             tiffOffset,
30955             tiffOffset + dirOffset,
30956             littleEndian
30957         );
30958     },
30959     
30960     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30961     {
30962         var tagsNumber,
30963             dirEndOffset,
30964             i;
30965         if (dirOffset + 6 > dataView.byteLength) {
30966             Roo.log('Invalid Exif data: Invalid directory offset.');
30967             return;
30968         }
30969         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30970         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30971         if (dirEndOffset + 4 > dataView.byteLength) {
30972             Roo.log('Invalid Exif data: Invalid directory size.');
30973             return;
30974         }
30975         for (i = 0; i < tagsNumber; i += 1) {
30976             this.parseExifTag(
30977                 dataView,
30978                 tiffOffset,
30979                 dirOffset + 2 + 12 * i, // tag offset
30980                 littleEndian
30981             );
30982         }
30983         // Return the offset to the next directory:
30984         return dataView.getUint32(dirEndOffset, littleEndian);
30985     },
30986     
30987     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30988     {
30989         var tag = dataView.getUint16(offset, littleEndian);
30990         
30991         this.exif[tag] = this.getExifValue(
30992             dataView,
30993             tiffOffset,
30994             offset,
30995             dataView.getUint16(offset + 2, littleEndian), // tag type
30996             dataView.getUint32(offset + 4, littleEndian), // tag length
30997             littleEndian
30998         );
30999     },
31000     
31001     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31002     {
31003         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31004             tagSize,
31005             dataOffset,
31006             values,
31007             i,
31008             str,
31009             c;
31010     
31011         if (!tagType) {
31012             Roo.log('Invalid Exif data: Invalid tag type.');
31013             return;
31014         }
31015         
31016         tagSize = tagType.size * length;
31017         // Determine if the value is contained in the dataOffset bytes,
31018         // or if the value at the dataOffset is a pointer to the actual data:
31019         dataOffset = tagSize > 4 ?
31020                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31021         if (dataOffset + tagSize > dataView.byteLength) {
31022             Roo.log('Invalid Exif data: Invalid data offset.');
31023             return;
31024         }
31025         if (length === 1) {
31026             return tagType.getValue(dataView, dataOffset, littleEndian);
31027         }
31028         values = [];
31029         for (i = 0; i < length; i += 1) {
31030             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31031         }
31032         
31033         if (tagType.ascii) {
31034             str = '';
31035             // Concatenate the chars:
31036             for (i = 0; i < values.length; i += 1) {
31037                 c = values[i];
31038                 // Ignore the terminating NULL byte(s):
31039                 if (c === '\u0000') {
31040                     break;
31041                 }
31042                 str += c;
31043             }
31044             return str;
31045         }
31046         return values;
31047     }
31048     
31049 });
31050
31051 Roo.apply(Roo.bootstrap.UploadCropbox, {
31052     tags : {
31053         'Orientation': 0x0112
31054     },
31055     
31056     Orientation: {
31057             1: 0, //'top-left',
31058 //            2: 'top-right',
31059             3: 180, //'bottom-right',
31060 //            4: 'bottom-left',
31061 //            5: 'left-top',
31062             6: 90, //'right-top',
31063 //            7: 'right-bottom',
31064             8: 270 //'left-bottom'
31065     },
31066     
31067     exifTagTypes : {
31068         // byte, 8-bit unsigned int:
31069         1: {
31070             getValue: function (dataView, dataOffset) {
31071                 return dataView.getUint8(dataOffset);
31072             },
31073             size: 1
31074         },
31075         // ascii, 8-bit byte:
31076         2: {
31077             getValue: function (dataView, dataOffset) {
31078                 return String.fromCharCode(dataView.getUint8(dataOffset));
31079             },
31080             size: 1,
31081             ascii: true
31082         },
31083         // short, 16 bit int:
31084         3: {
31085             getValue: function (dataView, dataOffset, littleEndian) {
31086                 return dataView.getUint16(dataOffset, littleEndian);
31087             },
31088             size: 2
31089         },
31090         // long, 32 bit int:
31091         4: {
31092             getValue: function (dataView, dataOffset, littleEndian) {
31093                 return dataView.getUint32(dataOffset, littleEndian);
31094             },
31095             size: 4
31096         },
31097         // rational = two long values, first is numerator, second is denominator:
31098         5: {
31099             getValue: function (dataView, dataOffset, littleEndian) {
31100                 return dataView.getUint32(dataOffset, littleEndian) /
31101                     dataView.getUint32(dataOffset + 4, littleEndian);
31102             },
31103             size: 8
31104         },
31105         // slong, 32 bit signed int:
31106         9: {
31107             getValue: function (dataView, dataOffset, littleEndian) {
31108                 return dataView.getInt32(dataOffset, littleEndian);
31109             },
31110             size: 4
31111         },
31112         // srational, two slongs, first is numerator, second is denominator:
31113         10: {
31114             getValue: function (dataView, dataOffset, littleEndian) {
31115                 return dataView.getInt32(dataOffset, littleEndian) /
31116                     dataView.getInt32(dataOffset + 4, littleEndian);
31117             },
31118             size: 8
31119         }
31120     },
31121     
31122     footer : {
31123         STANDARD : [
31124             {
31125                 tag : 'div',
31126                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31127                 action : 'rotate-left',
31128                 cn : [
31129                     {
31130                         tag : 'button',
31131                         cls : 'btn btn-default',
31132                         html : '<i class="fa fa-undo"></i>'
31133                     }
31134                 ]
31135             },
31136             {
31137                 tag : 'div',
31138                 cls : 'btn-group roo-upload-cropbox-picture',
31139                 action : 'picture',
31140                 cn : [
31141                     {
31142                         tag : 'button',
31143                         cls : 'btn btn-default',
31144                         html : '<i class="fa fa-picture-o"></i>'
31145                     }
31146                 ]
31147             },
31148             {
31149                 tag : 'div',
31150                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31151                 action : 'rotate-right',
31152                 cn : [
31153                     {
31154                         tag : 'button',
31155                         cls : 'btn btn-default',
31156                         html : '<i class="fa fa-repeat"></i>'
31157                     }
31158                 ]
31159             }
31160         ],
31161         DOCUMENT : [
31162             {
31163                 tag : 'div',
31164                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31165                 action : 'rotate-left',
31166                 cn : [
31167                     {
31168                         tag : 'button',
31169                         cls : 'btn btn-default',
31170                         html : '<i class="fa fa-undo"></i>'
31171                     }
31172                 ]
31173             },
31174             {
31175                 tag : 'div',
31176                 cls : 'btn-group roo-upload-cropbox-download',
31177                 action : 'download',
31178                 cn : [
31179                     {
31180                         tag : 'button',
31181                         cls : 'btn btn-default',
31182                         html : '<i class="fa fa-download"></i>'
31183                     }
31184                 ]
31185             },
31186             {
31187                 tag : 'div',
31188                 cls : 'btn-group roo-upload-cropbox-crop',
31189                 action : 'crop',
31190                 cn : [
31191                     {
31192                         tag : 'button',
31193                         cls : 'btn btn-default',
31194                         html : '<i class="fa fa-crop"></i>'
31195                     }
31196                 ]
31197             },
31198             {
31199                 tag : 'div',
31200                 cls : 'btn-group roo-upload-cropbox-trash',
31201                 action : 'trash',
31202                 cn : [
31203                     {
31204                         tag : 'button',
31205                         cls : 'btn btn-default',
31206                         html : '<i class="fa fa-trash"></i>'
31207                     }
31208                 ]
31209             },
31210             {
31211                 tag : 'div',
31212                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31213                 action : 'rotate-right',
31214                 cn : [
31215                     {
31216                         tag : 'button',
31217                         cls : 'btn btn-default',
31218                         html : '<i class="fa fa-repeat"></i>'
31219                     }
31220                 ]
31221             }
31222         ],
31223         ROTATOR : [
31224             {
31225                 tag : 'div',
31226                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31227                 action : 'rotate-left',
31228                 cn : [
31229                     {
31230                         tag : 'button',
31231                         cls : 'btn btn-default',
31232                         html : '<i class="fa fa-undo"></i>'
31233                     }
31234                 ]
31235             },
31236             {
31237                 tag : 'div',
31238                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31239                 action : 'rotate-right',
31240                 cn : [
31241                     {
31242                         tag : 'button',
31243                         cls : 'btn btn-default',
31244                         html : '<i class="fa fa-repeat"></i>'
31245                     }
31246                 ]
31247             }
31248         ]
31249     }
31250 });
31251
31252 /*
31253 * Licence: LGPL
31254 */
31255
31256 /**
31257  * @class Roo.bootstrap.DocumentManager
31258  * @extends Roo.bootstrap.Component
31259  * Bootstrap DocumentManager class
31260  * @cfg {String} paramName default 'imageUpload'
31261  * @cfg {String} toolTipName default 'filename'
31262  * @cfg {String} method default POST
31263  * @cfg {String} url action url
31264  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31265  * @cfg {Boolean} multiple multiple upload default true
31266  * @cfg {Number} thumbSize default 300
31267  * @cfg {String} fieldLabel
31268  * @cfg {Number} labelWidth default 4
31269  * @cfg {String} labelAlign (left|top) default left
31270  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31271 * @cfg {Number} labellg set the width of label (1-12)
31272  * @cfg {Number} labelmd set the width of label (1-12)
31273  * @cfg {Number} labelsm set the width of label (1-12)
31274  * @cfg {Number} labelxs set the width of label (1-12)
31275  * 
31276  * @constructor
31277  * Create a new DocumentManager
31278  * @param {Object} config The config object
31279  */
31280
31281 Roo.bootstrap.DocumentManager = function(config){
31282     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31283     
31284     this.files = [];
31285     this.delegates = [];
31286     
31287     this.addEvents({
31288         /**
31289          * @event initial
31290          * Fire when initial the DocumentManager
31291          * @param {Roo.bootstrap.DocumentManager} this
31292          */
31293         "initial" : true,
31294         /**
31295          * @event inspect
31296          * inspect selected file
31297          * @param {Roo.bootstrap.DocumentManager} this
31298          * @param {File} file
31299          */
31300         "inspect" : true,
31301         /**
31302          * @event exception
31303          * Fire when xhr load exception
31304          * @param {Roo.bootstrap.DocumentManager} this
31305          * @param {XMLHttpRequest} xhr
31306          */
31307         "exception" : true,
31308         /**
31309          * @event afterupload
31310          * Fire when xhr load exception
31311          * @param {Roo.bootstrap.DocumentManager} this
31312          * @param {XMLHttpRequest} xhr
31313          */
31314         "afterupload" : true,
31315         /**
31316          * @event prepare
31317          * prepare the form data
31318          * @param {Roo.bootstrap.DocumentManager} this
31319          * @param {Object} formData
31320          */
31321         "prepare" : true,
31322         /**
31323          * @event remove
31324          * Fire when remove the file
31325          * @param {Roo.bootstrap.DocumentManager} this
31326          * @param {Object} file
31327          */
31328         "remove" : true,
31329         /**
31330          * @event refresh
31331          * Fire after refresh the file
31332          * @param {Roo.bootstrap.DocumentManager} this
31333          */
31334         "refresh" : true,
31335         /**
31336          * @event click
31337          * Fire after click the image
31338          * @param {Roo.bootstrap.DocumentManager} this
31339          * @param {Object} file
31340          */
31341         "click" : true,
31342         /**
31343          * @event edit
31344          * Fire when upload a image and editable set to true
31345          * @param {Roo.bootstrap.DocumentManager} this
31346          * @param {Object} file
31347          */
31348         "edit" : true,
31349         /**
31350          * @event beforeselectfile
31351          * Fire before select file
31352          * @param {Roo.bootstrap.DocumentManager} this
31353          */
31354         "beforeselectfile" : true,
31355         /**
31356          * @event process
31357          * Fire before process file
31358          * @param {Roo.bootstrap.DocumentManager} this
31359          * @param {Object} file
31360          */
31361         "process" : true,
31362         /**
31363          * @event previewrendered
31364          * Fire when preview rendered
31365          * @param {Roo.bootstrap.DocumentManager} this
31366          * @param {Object} file
31367          */
31368         "previewrendered" : true,
31369         /**
31370          */
31371         "previewResize" : true
31372         
31373     });
31374 };
31375
31376 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31377     
31378     boxes : 0,
31379     inputName : '',
31380     thumbSize : 300,
31381     multiple : true,
31382     files : false,
31383     method : 'POST',
31384     url : '',
31385     paramName : 'imageUpload',
31386     toolTipName : 'filename',
31387     fieldLabel : '',
31388     labelWidth : 4,
31389     labelAlign : 'left',
31390     editable : true,
31391     delegates : false,
31392     xhr : false, 
31393     
31394     labellg : 0,
31395     labelmd : 0,
31396     labelsm : 0,
31397     labelxs : 0,
31398     
31399     getAutoCreate : function()
31400     {   
31401         var managerWidget = {
31402             tag : 'div',
31403             cls : 'roo-document-manager',
31404             cn : [
31405                 {
31406                     tag : 'input',
31407                     cls : 'roo-document-manager-selector',
31408                     type : 'file'
31409                 },
31410                 {
31411                     tag : 'div',
31412                     cls : 'roo-document-manager-uploader',
31413                     cn : [
31414                         {
31415                             tag : 'div',
31416                             cls : 'roo-document-manager-upload-btn',
31417                             html : '<i class="fa fa-plus"></i>'
31418                         }
31419                     ]
31420                     
31421                 }
31422             ]
31423         };
31424         
31425         var content = [
31426             {
31427                 tag : 'div',
31428                 cls : 'column col-md-12',
31429                 cn : managerWidget
31430             }
31431         ];
31432         
31433         if(this.fieldLabel.length){
31434             
31435             content = [
31436                 {
31437                     tag : 'div',
31438                     cls : 'column col-md-12',
31439                     html : this.fieldLabel
31440                 },
31441                 {
31442                     tag : 'div',
31443                     cls : 'column col-md-12',
31444                     cn : managerWidget
31445                 }
31446             ];
31447
31448             if(this.labelAlign == 'left'){
31449                 content = [
31450                     {
31451                         tag : 'div',
31452                         cls : 'column',
31453                         html : this.fieldLabel
31454                     },
31455                     {
31456                         tag : 'div',
31457                         cls : 'column',
31458                         cn : managerWidget
31459                     }
31460                 ];
31461                 
31462                 if(this.labelWidth > 12){
31463                     content[0].style = "width: " + this.labelWidth + 'px';
31464                 }
31465
31466                 if(this.labelWidth < 13 && this.labelmd == 0){
31467                     this.labelmd = this.labelWidth;
31468                 }
31469
31470                 if(this.labellg > 0){
31471                     content[0].cls += ' col-lg-' + this.labellg;
31472                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31473                 }
31474
31475                 if(this.labelmd > 0){
31476                     content[0].cls += ' col-md-' + this.labelmd;
31477                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31478                 }
31479
31480                 if(this.labelsm > 0){
31481                     content[0].cls += ' col-sm-' + this.labelsm;
31482                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31483                 }
31484
31485                 if(this.labelxs > 0){
31486                     content[0].cls += ' col-xs-' + this.labelxs;
31487                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31488                 }
31489                 
31490             }
31491         }
31492         
31493         var cfg = {
31494             tag : 'div',
31495             cls : 'row clearfix',
31496             cn : content
31497         };
31498         
31499         return cfg;
31500         
31501     },
31502     
31503     initEvents : function()
31504     {
31505         this.managerEl = this.el.select('.roo-document-manager', true).first();
31506         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31507         
31508         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31509         this.selectorEl.hide();
31510         
31511         if(this.multiple){
31512             this.selectorEl.attr('multiple', 'multiple');
31513         }
31514         
31515         this.selectorEl.on('change', this.onFileSelected, this);
31516         
31517         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31518         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31519         
31520         this.uploader.on('click', this.onUploaderClick, this);
31521         
31522         this.renderProgressDialog();
31523         
31524         var _this = this;
31525         
31526         window.addEventListener("resize", function() { _this.refresh(); } );
31527         
31528         this.fireEvent('initial', this);
31529     },
31530     
31531     renderProgressDialog : function()
31532     {
31533         var _this = this;
31534         
31535         this.progressDialog = new Roo.bootstrap.Modal({
31536             cls : 'roo-document-manager-progress-dialog',
31537             allow_close : false,
31538             animate : false,
31539             title : '',
31540             buttons : [
31541                 {
31542                     name  :'cancel',
31543                     weight : 'danger',
31544                     html : 'Cancel'
31545                 }
31546             ], 
31547             listeners : { 
31548                 btnclick : function() {
31549                     _this.uploadCancel();
31550                     this.hide();
31551                 }
31552             }
31553         });
31554          
31555         this.progressDialog.render(Roo.get(document.body));
31556          
31557         this.progress = new Roo.bootstrap.Progress({
31558             cls : 'roo-document-manager-progress',
31559             active : true,
31560             striped : true
31561         });
31562         
31563         this.progress.render(this.progressDialog.getChildContainer());
31564         
31565         this.progressBar = new Roo.bootstrap.ProgressBar({
31566             cls : 'roo-document-manager-progress-bar',
31567             aria_valuenow : 0,
31568             aria_valuemin : 0,
31569             aria_valuemax : 12,
31570             panel : 'success'
31571         });
31572         
31573         this.progressBar.render(this.progress.getChildContainer());
31574     },
31575     
31576     onUploaderClick : function(e)
31577     {
31578         e.preventDefault();
31579      
31580         if(this.fireEvent('beforeselectfile', this) != false){
31581             this.selectorEl.dom.click();
31582         }
31583         
31584     },
31585     
31586     onFileSelected : function(e)
31587     {
31588         e.preventDefault();
31589         
31590         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31591             return;
31592         }
31593         
31594         Roo.each(this.selectorEl.dom.files, function(file){
31595             if(this.fireEvent('inspect', this, file) != false){
31596                 this.files.push(file);
31597             }
31598         }, this);
31599         
31600         this.queue();
31601         
31602     },
31603     
31604     queue : function()
31605     {
31606         this.selectorEl.dom.value = '';
31607         
31608         if(!this.files || !this.files.length){
31609             return;
31610         }
31611         
31612         if(this.boxes > 0 && this.files.length > this.boxes){
31613             this.files = this.files.slice(0, this.boxes);
31614         }
31615         
31616         this.uploader.show();
31617         
31618         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31619             this.uploader.hide();
31620         }
31621         
31622         var _this = this;
31623         
31624         var files = [];
31625         
31626         var docs = [];
31627         
31628         Roo.each(this.files, function(file){
31629             
31630             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31631                 var f = this.renderPreview(file);
31632                 files.push(f);
31633                 return;
31634             }
31635             
31636             if(file.type.indexOf('image') != -1){
31637                 this.delegates.push(
31638                     (function(){
31639                         _this.process(file);
31640                     }).createDelegate(this)
31641                 );
31642         
31643                 return;
31644             }
31645             
31646             docs.push(
31647                 (function(){
31648                     _this.process(file);
31649                 }).createDelegate(this)
31650             );
31651             
31652         }, this);
31653         
31654         this.files = files;
31655         
31656         this.delegates = this.delegates.concat(docs);
31657         
31658         if(!this.delegates.length){
31659             this.refresh();
31660             return;
31661         }
31662         
31663         this.progressBar.aria_valuemax = this.delegates.length;
31664         
31665         this.arrange();
31666         
31667         return;
31668     },
31669     
31670     arrange : function()
31671     {
31672         if(!this.delegates.length){
31673             this.progressDialog.hide();
31674             this.refresh();
31675             return;
31676         }
31677         
31678         var delegate = this.delegates.shift();
31679         
31680         this.progressDialog.show();
31681         
31682         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31683         
31684         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31685         
31686         delegate();
31687     },
31688     
31689     refresh : function()
31690     {
31691         this.uploader.show();
31692         
31693         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31694             this.uploader.hide();
31695         }
31696         
31697         Roo.isTouch ? this.closable(false) : this.closable(true);
31698         
31699         this.fireEvent('refresh', this);
31700     },
31701     
31702     onRemove : function(e, el, o)
31703     {
31704         e.preventDefault();
31705         
31706         this.fireEvent('remove', this, o);
31707         
31708     },
31709     
31710     remove : function(o)
31711     {
31712         var files = [];
31713         
31714         Roo.each(this.files, function(file){
31715             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31716                 files.push(file);
31717                 return;
31718             }
31719
31720             o.target.remove();
31721
31722         }, this);
31723         
31724         this.files = files;
31725         
31726         this.refresh();
31727     },
31728     
31729     clear : function()
31730     {
31731         Roo.each(this.files, function(file){
31732             if(!file.target){
31733                 return;
31734             }
31735             
31736             file.target.remove();
31737
31738         }, this);
31739         
31740         this.files = [];
31741         
31742         this.refresh();
31743     },
31744     
31745     onClick : function(e, el, o)
31746     {
31747         e.preventDefault();
31748         
31749         this.fireEvent('click', this, o);
31750         
31751     },
31752     
31753     closable : function(closable)
31754     {
31755         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31756             
31757             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31758             
31759             if(closable){
31760                 el.show();
31761                 return;
31762             }
31763             
31764             el.hide();
31765             
31766         }, this);
31767     },
31768     
31769     xhrOnLoad : function(xhr)
31770     {
31771         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31772             el.remove();
31773         }, this);
31774         
31775         if (xhr.readyState !== 4) {
31776             this.arrange();
31777             this.fireEvent('exception', this, xhr);
31778             return;
31779         }
31780
31781         var response = Roo.decode(xhr.responseText);
31782         
31783         if(!response.success){
31784             this.arrange();
31785             this.fireEvent('exception', this, xhr);
31786             return;
31787         }
31788         
31789         var file = this.renderPreview(response.data);
31790         
31791         this.files.push(file);
31792         
31793         this.arrange();
31794         
31795         this.fireEvent('afterupload', this, xhr);
31796         
31797     },
31798     
31799     xhrOnError : function(xhr)
31800     {
31801         Roo.log('xhr on error');
31802         
31803         var response = Roo.decode(xhr.responseText);
31804           
31805         Roo.log(response);
31806         
31807         this.arrange();
31808     },
31809     
31810     process : function(file)
31811     {
31812         if(this.fireEvent('process', this, file) !== false){
31813             if(this.editable && file.type.indexOf('image') != -1){
31814                 this.fireEvent('edit', this, file);
31815                 return;
31816             }
31817
31818             this.uploadStart(file, false);
31819
31820             return;
31821         }
31822         
31823     },
31824     
31825     uploadStart : function(file, crop)
31826     {
31827         this.xhr = new XMLHttpRequest();
31828         
31829         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31830             this.arrange();
31831             return;
31832         }
31833         
31834         file.xhr = this.xhr;
31835             
31836         this.managerEl.createChild({
31837             tag : 'div',
31838             cls : 'roo-document-manager-loading',
31839             cn : [
31840                 {
31841                     tag : 'div',
31842                     tooltip : file.name,
31843                     cls : 'roo-document-manager-thumb',
31844                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31845                 }
31846             ]
31847
31848         });
31849
31850         this.xhr.open(this.method, this.url, true);
31851         
31852         var headers = {
31853             "Accept": "application/json",
31854             "Cache-Control": "no-cache",
31855             "X-Requested-With": "XMLHttpRequest"
31856         };
31857         
31858         for (var headerName in headers) {
31859             var headerValue = headers[headerName];
31860             if (headerValue) {
31861                 this.xhr.setRequestHeader(headerName, headerValue);
31862             }
31863         }
31864         
31865         var _this = this;
31866         
31867         this.xhr.onload = function()
31868         {
31869             _this.xhrOnLoad(_this.xhr);
31870         }
31871         
31872         this.xhr.onerror = function()
31873         {
31874             _this.xhrOnError(_this.xhr);
31875         }
31876         
31877         var formData = new FormData();
31878
31879         formData.append('returnHTML', 'NO');
31880         
31881         if(crop){
31882             formData.append('crop', crop);
31883         }
31884         
31885         formData.append(this.paramName, file, file.name);
31886         
31887         var options = {
31888             file : file, 
31889             manually : false
31890         };
31891         
31892         if(this.fireEvent('prepare', this, formData, options) != false){
31893             
31894             if(options.manually){
31895                 return;
31896             }
31897             
31898             this.xhr.send(formData);
31899             return;
31900         };
31901         
31902         this.uploadCancel();
31903     },
31904     
31905     uploadCancel : function()
31906     {
31907         if (this.xhr) {
31908             this.xhr.abort();
31909         }
31910         
31911         this.delegates = [];
31912         
31913         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31914             el.remove();
31915         }, this);
31916         
31917         this.arrange();
31918     },
31919     
31920     renderPreview : function(file)
31921     {
31922         if(typeof(file.target) != 'undefined' && file.target){
31923             return file;
31924         }
31925         
31926         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31927         
31928         var previewEl = this.managerEl.createChild({
31929             tag : 'div',
31930             cls : 'roo-document-manager-preview',
31931             cn : [
31932                 {
31933                     tag : 'div',
31934                     tooltip : file[this.toolTipName],
31935                     cls : 'roo-document-manager-thumb',
31936                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31937                 },
31938                 {
31939                     tag : 'button',
31940                     cls : 'close',
31941                     html : '<i class="fa fa-times-circle"></i>'
31942                 }
31943             ]
31944         });
31945
31946         var close = previewEl.select('button.close', true).first();
31947
31948         close.on('click', this.onRemove, this, file);
31949
31950         file.target = previewEl;
31951
31952         var image = previewEl.select('img', true).first();
31953         
31954         var _this = this;
31955         
31956         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31957         
31958         image.on('click', this.onClick, this, file);
31959         
31960         this.fireEvent('previewrendered', this, file);
31961         
31962         return file;
31963         
31964     },
31965     
31966     onPreviewLoad : function(file, image)
31967     {
31968         if(typeof(file.target) == 'undefined' || !file.target){
31969             return;
31970         }
31971         
31972         var width = image.dom.naturalWidth || image.dom.width;
31973         var height = image.dom.naturalHeight || image.dom.height;
31974         
31975         if(!this.previewResize) {
31976             return;
31977         }
31978         
31979         if(width > height){
31980             file.target.addClass('wide');
31981             return;
31982         }
31983         
31984         file.target.addClass('tall');
31985         return;
31986         
31987     },
31988     
31989     uploadFromSource : function(file, crop)
31990     {
31991         this.xhr = new XMLHttpRequest();
31992         
31993         this.managerEl.createChild({
31994             tag : 'div',
31995             cls : 'roo-document-manager-loading',
31996             cn : [
31997                 {
31998                     tag : 'div',
31999                     tooltip : file.name,
32000                     cls : 'roo-document-manager-thumb',
32001                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32002                 }
32003             ]
32004
32005         });
32006
32007         this.xhr.open(this.method, this.url, true);
32008         
32009         var headers = {
32010             "Accept": "application/json",
32011             "Cache-Control": "no-cache",
32012             "X-Requested-With": "XMLHttpRequest"
32013         };
32014         
32015         for (var headerName in headers) {
32016             var headerValue = headers[headerName];
32017             if (headerValue) {
32018                 this.xhr.setRequestHeader(headerName, headerValue);
32019             }
32020         }
32021         
32022         var _this = this;
32023         
32024         this.xhr.onload = function()
32025         {
32026             _this.xhrOnLoad(_this.xhr);
32027         }
32028         
32029         this.xhr.onerror = function()
32030         {
32031             _this.xhrOnError(_this.xhr);
32032         }
32033         
32034         var formData = new FormData();
32035
32036         formData.append('returnHTML', 'NO');
32037         
32038         formData.append('crop', crop);
32039         
32040         if(typeof(file.filename) != 'undefined'){
32041             formData.append('filename', file.filename);
32042         }
32043         
32044         if(typeof(file.mimetype) != 'undefined'){
32045             formData.append('mimetype', file.mimetype);
32046         }
32047         
32048         Roo.log(formData);
32049         
32050         if(this.fireEvent('prepare', this, formData) != false){
32051             this.xhr.send(formData);
32052         };
32053     }
32054 });
32055
32056 /*
32057 * Licence: LGPL
32058 */
32059
32060 /**
32061  * @class Roo.bootstrap.DocumentViewer
32062  * @extends Roo.bootstrap.Component
32063  * Bootstrap DocumentViewer class
32064  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32065  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32066  * 
32067  * @constructor
32068  * Create a new DocumentViewer
32069  * @param {Object} config The config object
32070  */
32071
32072 Roo.bootstrap.DocumentViewer = function(config){
32073     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32074     
32075     this.addEvents({
32076         /**
32077          * @event initial
32078          * Fire after initEvent
32079          * @param {Roo.bootstrap.DocumentViewer} this
32080          */
32081         "initial" : true,
32082         /**
32083          * @event click
32084          * Fire after click
32085          * @param {Roo.bootstrap.DocumentViewer} this
32086          */
32087         "click" : true,
32088         /**
32089          * @event download
32090          * Fire after download button
32091          * @param {Roo.bootstrap.DocumentViewer} this
32092          */
32093         "download" : true,
32094         /**
32095          * @event trash
32096          * Fire after trash button
32097          * @param {Roo.bootstrap.DocumentViewer} this
32098          */
32099         "trash" : true
32100         
32101     });
32102 };
32103
32104 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32105     
32106     showDownload : true,
32107     
32108     showTrash : true,
32109     
32110     getAutoCreate : function()
32111     {
32112         var cfg = {
32113             tag : 'div',
32114             cls : 'roo-document-viewer',
32115             cn : [
32116                 {
32117                     tag : 'div',
32118                     cls : 'roo-document-viewer-body',
32119                     cn : [
32120                         {
32121                             tag : 'div',
32122                             cls : 'roo-document-viewer-thumb',
32123                             cn : [
32124                                 {
32125                                     tag : 'img',
32126                                     cls : 'roo-document-viewer-image'
32127                                 }
32128                             ]
32129                         }
32130                     ]
32131                 },
32132                 {
32133                     tag : 'div',
32134                     cls : 'roo-document-viewer-footer',
32135                     cn : {
32136                         tag : 'div',
32137                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32138                         cn : [
32139                             {
32140                                 tag : 'div',
32141                                 cls : 'btn-group roo-document-viewer-download',
32142                                 cn : [
32143                                     {
32144                                         tag : 'button',
32145                                         cls : 'btn btn-default',
32146                                         html : '<i class="fa fa-download"></i>'
32147                                     }
32148                                 ]
32149                             },
32150                             {
32151                                 tag : 'div',
32152                                 cls : 'btn-group roo-document-viewer-trash',
32153                                 cn : [
32154                                     {
32155                                         tag : 'button',
32156                                         cls : 'btn btn-default',
32157                                         html : '<i class="fa fa-trash"></i>'
32158                                     }
32159                                 ]
32160                             }
32161                         ]
32162                     }
32163                 }
32164             ]
32165         };
32166         
32167         return cfg;
32168     },
32169     
32170     initEvents : function()
32171     {
32172         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32173         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32174         
32175         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32176         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32177         
32178         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32179         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32180         
32181         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32182         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32183         
32184         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32185         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32186         
32187         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32188         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32189         
32190         this.bodyEl.on('click', this.onClick, this);
32191         this.downloadBtn.on('click', this.onDownload, this);
32192         this.trashBtn.on('click', this.onTrash, this);
32193         
32194         this.downloadBtn.hide();
32195         this.trashBtn.hide();
32196         
32197         if(this.showDownload){
32198             this.downloadBtn.show();
32199         }
32200         
32201         if(this.showTrash){
32202             this.trashBtn.show();
32203         }
32204         
32205         if(!this.showDownload && !this.showTrash) {
32206             this.footerEl.hide();
32207         }
32208         
32209     },
32210     
32211     initial : function()
32212     {
32213         this.fireEvent('initial', this);
32214         
32215     },
32216     
32217     onClick : function(e)
32218     {
32219         e.preventDefault();
32220         
32221         this.fireEvent('click', this);
32222     },
32223     
32224     onDownload : function(e)
32225     {
32226         e.preventDefault();
32227         
32228         this.fireEvent('download', this);
32229     },
32230     
32231     onTrash : function(e)
32232     {
32233         e.preventDefault();
32234         
32235         this.fireEvent('trash', this);
32236     }
32237     
32238 });
32239 /*
32240  * - LGPL
32241  *
32242  * nav progress bar
32243  * 
32244  */
32245
32246 /**
32247  * @class Roo.bootstrap.NavProgressBar
32248  * @extends Roo.bootstrap.Component
32249  * Bootstrap NavProgressBar class
32250  * 
32251  * @constructor
32252  * Create a new nav progress bar
32253  * @param {Object} config The config object
32254  */
32255
32256 Roo.bootstrap.NavProgressBar = function(config){
32257     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32258
32259     this.bullets = this.bullets || [];
32260    
32261 //    Roo.bootstrap.NavProgressBar.register(this);
32262      this.addEvents({
32263         /**
32264              * @event changed
32265              * Fires when the active item changes
32266              * @param {Roo.bootstrap.NavProgressBar} this
32267              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32268              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32269          */
32270         'changed': true
32271      });
32272     
32273 };
32274
32275 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32276     
32277     bullets : [],
32278     barItems : [],
32279     
32280     getAutoCreate : function()
32281     {
32282         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32283         
32284         cfg = {
32285             tag : 'div',
32286             cls : 'roo-navigation-bar-group',
32287             cn : [
32288                 {
32289                     tag : 'div',
32290                     cls : 'roo-navigation-top-bar'
32291                 },
32292                 {
32293                     tag : 'div',
32294                     cls : 'roo-navigation-bullets-bar',
32295                     cn : [
32296                         {
32297                             tag : 'ul',
32298                             cls : 'roo-navigation-bar'
32299                         }
32300                     ]
32301                 },
32302                 
32303                 {
32304                     tag : 'div',
32305                     cls : 'roo-navigation-bottom-bar'
32306                 }
32307             ]
32308             
32309         };
32310         
32311         return cfg;
32312         
32313     },
32314     
32315     initEvents: function() 
32316     {
32317         
32318     },
32319     
32320     onRender : function(ct, position) 
32321     {
32322         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32323         
32324         if(this.bullets.length){
32325             Roo.each(this.bullets, function(b){
32326                this.addItem(b);
32327             }, this);
32328         }
32329         
32330         this.format();
32331         
32332     },
32333     
32334     addItem : function(cfg)
32335     {
32336         var item = new Roo.bootstrap.NavProgressItem(cfg);
32337         
32338         item.parentId = this.id;
32339         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32340         
32341         if(cfg.html){
32342             var top = new Roo.bootstrap.Element({
32343                 tag : 'div',
32344                 cls : 'roo-navigation-bar-text'
32345             });
32346             
32347             var bottom = new Roo.bootstrap.Element({
32348                 tag : 'div',
32349                 cls : 'roo-navigation-bar-text'
32350             });
32351             
32352             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32353             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32354             
32355             var topText = new Roo.bootstrap.Element({
32356                 tag : 'span',
32357                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32358             });
32359             
32360             var bottomText = new Roo.bootstrap.Element({
32361                 tag : 'span',
32362                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32363             });
32364             
32365             topText.onRender(top.el, null);
32366             bottomText.onRender(bottom.el, null);
32367             
32368             item.topEl = top;
32369             item.bottomEl = bottom;
32370         }
32371         
32372         this.barItems.push(item);
32373         
32374         return item;
32375     },
32376     
32377     getActive : function()
32378     {
32379         var active = false;
32380         
32381         Roo.each(this.barItems, function(v){
32382             
32383             if (!v.isActive()) {
32384                 return;
32385             }
32386             
32387             active = v;
32388             return false;
32389             
32390         });
32391         
32392         return active;
32393     },
32394     
32395     setActiveItem : function(item)
32396     {
32397         var prev = false;
32398         
32399         Roo.each(this.barItems, function(v){
32400             if (v.rid == item.rid) {
32401                 return ;
32402             }
32403             
32404             if (v.isActive()) {
32405                 v.setActive(false);
32406                 prev = v;
32407             }
32408         });
32409
32410         item.setActive(true);
32411         
32412         this.fireEvent('changed', this, item, prev);
32413     },
32414     
32415     getBarItem: function(rid)
32416     {
32417         var ret = false;
32418         
32419         Roo.each(this.barItems, function(e) {
32420             if (e.rid != rid) {
32421                 return;
32422             }
32423             
32424             ret =  e;
32425             return false;
32426         });
32427         
32428         return ret;
32429     },
32430     
32431     indexOfItem : function(item)
32432     {
32433         var index = false;
32434         
32435         Roo.each(this.barItems, function(v, i){
32436             
32437             if (v.rid != item.rid) {
32438                 return;
32439             }
32440             
32441             index = i;
32442             return false
32443         });
32444         
32445         return index;
32446     },
32447     
32448     setActiveNext : function()
32449     {
32450         var i = this.indexOfItem(this.getActive());
32451         
32452         if (i > this.barItems.length) {
32453             return;
32454         }
32455         
32456         this.setActiveItem(this.barItems[i+1]);
32457     },
32458     
32459     setActivePrev : function()
32460     {
32461         var i = this.indexOfItem(this.getActive());
32462         
32463         if (i  < 1) {
32464             return;
32465         }
32466         
32467         this.setActiveItem(this.barItems[i-1]);
32468     },
32469     
32470     format : function()
32471     {
32472         if(!this.barItems.length){
32473             return;
32474         }
32475      
32476         var width = 100 / this.barItems.length;
32477         
32478         Roo.each(this.barItems, function(i){
32479             i.el.setStyle('width', width + '%');
32480             i.topEl.el.setStyle('width', width + '%');
32481             i.bottomEl.el.setStyle('width', width + '%');
32482         }, this);
32483         
32484     }
32485     
32486 });
32487 /*
32488  * - LGPL
32489  *
32490  * Nav Progress Item
32491  * 
32492  */
32493
32494 /**
32495  * @class Roo.bootstrap.NavProgressItem
32496  * @extends Roo.bootstrap.Component
32497  * Bootstrap NavProgressItem class
32498  * @cfg {String} rid the reference id
32499  * @cfg {Boolean} active (true|false) Is item active default false
32500  * @cfg {Boolean} disabled (true|false) Is item active default false
32501  * @cfg {String} html
32502  * @cfg {String} position (top|bottom) text position default bottom
32503  * @cfg {String} icon show icon instead of number
32504  * 
32505  * @constructor
32506  * Create a new NavProgressItem
32507  * @param {Object} config The config object
32508  */
32509 Roo.bootstrap.NavProgressItem = function(config){
32510     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32511     this.addEvents({
32512         // raw events
32513         /**
32514          * @event click
32515          * The raw click event for the entire grid.
32516          * @param {Roo.bootstrap.NavProgressItem} this
32517          * @param {Roo.EventObject} e
32518          */
32519         "click" : true
32520     });
32521    
32522 };
32523
32524 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32525     
32526     rid : '',
32527     active : false,
32528     disabled : false,
32529     html : '',
32530     position : 'bottom',
32531     icon : false,
32532     
32533     getAutoCreate : function()
32534     {
32535         var iconCls = 'roo-navigation-bar-item-icon';
32536         
32537         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32538         
32539         var cfg = {
32540             tag: 'li',
32541             cls: 'roo-navigation-bar-item',
32542             cn : [
32543                 {
32544                     tag : 'i',
32545                     cls : iconCls
32546                 }
32547             ]
32548         };
32549         
32550         if(this.active){
32551             cfg.cls += ' active';
32552         }
32553         if(this.disabled){
32554             cfg.cls += ' disabled';
32555         }
32556         
32557         return cfg;
32558     },
32559     
32560     disable : function()
32561     {
32562         this.setDisabled(true);
32563     },
32564     
32565     enable : function()
32566     {
32567         this.setDisabled(false);
32568     },
32569     
32570     initEvents: function() 
32571     {
32572         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32573         
32574         this.iconEl.on('click', this.onClick, this);
32575     },
32576     
32577     onClick : function(e)
32578     {
32579         e.preventDefault();
32580         
32581         if(this.disabled){
32582             return;
32583         }
32584         
32585         if(this.fireEvent('click', this, e) === false){
32586             return;
32587         };
32588         
32589         this.parent().setActiveItem(this);
32590     },
32591     
32592     isActive: function () 
32593     {
32594         return this.active;
32595     },
32596     
32597     setActive : function(state)
32598     {
32599         if(this.active == state){
32600             return;
32601         }
32602         
32603         this.active = state;
32604         
32605         if (state) {
32606             this.el.addClass('active');
32607             return;
32608         }
32609         
32610         this.el.removeClass('active');
32611         
32612         return;
32613     },
32614     
32615     setDisabled : function(state)
32616     {
32617         if(this.disabled == state){
32618             return;
32619         }
32620         
32621         this.disabled = state;
32622         
32623         if (state) {
32624             this.el.addClass('disabled');
32625             return;
32626         }
32627         
32628         this.el.removeClass('disabled');
32629     },
32630     
32631     tooltipEl : function()
32632     {
32633         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32634     }
32635 });
32636  
32637
32638  /*
32639  * - LGPL
32640  *
32641  * FieldLabel
32642  * 
32643  */
32644
32645 /**
32646  * @class Roo.bootstrap.FieldLabel
32647  * @extends Roo.bootstrap.Component
32648  * Bootstrap FieldLabel class
32649  * @cfg {String} html contents of the element
32650  * @cfg {String} tag tag of the element default label
32651  * @cfg {String} cls class of the element
32652  * @cfg {String} target label target 
32653  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32654  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32655  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32656  * @cfg {String} iconTooltip default "This field is required"
32657  * @cfg {String} indicatorpos (left|right) default left
32658  * 
32659  * @constructor
32660  * Create a new FieldLabel
32661  * @param {Object} config The config object
32662  */
32663
32664 Roo.bootstrap.FieldLabel = function(config){
32665     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32666     
32667     this.addEvents({
32668             /**
32669              * @event invalid
32670              * Fires after the field has been marked as invalid.
32671              * @param {Roo.form.FieldLabel} this
32672              * @param {String} msg The validation message
32673              */
32674             invalid : true,
32675             /**
32676              * @event valid
32677              * Fires after the field has been validated with no errors.
32678              * @param {Roo.form.FieldLabel} this
32679              */
32680             valid : true
32681         });
32682 };
32683
32684 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32685     
32686     tag: 'label',
32687     cls: '',
32688     html: '',
32689     target: '',
32690     allowBlank : true,
32691     invalidClass : 'has-warning',
32692     validClass : 'has-success',
32693     iconTooltip : 'This field is required',
32694     indicatorpos : 'left',
32695     
32696     getAutoCreate : function(){
32697         
32698         var cls = "";
32699         if (!this.allowBlank) {
32700             cls  = "visible";
32701         }
32702         
32703         var cfg = {
32704             tag : this.tag,
32705             cls : 'roo-bootstrap-field-label ' + this.cls,
32706             for : this.target,
32707             cn : [
32708                 {
32709                     tag : 'i',
32710                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32711                     tooltip : this.iconTooltip
32712                 },
32713                 {
32714                     tag : 'span',
32715                     html : this.html
32716                 }
32717             ] 
32718         };
32719         
32720         if(this.indicatorpos == 'right'){
32721             var cfg = {
32722                 tag : this.tag,
32723                 cls : 'roo-bootstrap-field-label ' + this.cls,
32724                 for : this.target,
32725                 cn : [
32726                     {
32727                         tag : 'span',
32728                         html : this.html
32729                     },
32730                     {
32731                         tag : 'i',
32732                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32733                         tooltip : this.iconTooltip
32734                     }
32735                 ] 
32736             };
32737         }
32738         
32739         return cfg;
32740     },
32741     
32742     initEvents: function() 
32743     {
32744         Roo.bootstrap.Element.superclass.initEvents.call(this);
32745         
32746         this.indicator = this.indicatorEl();
32747         
32748         if(this.indicator){
32749             this.indicator.removeClass('visible');
32750             this.indicator.addClass('invisible');
32751         }
32752         
32753         Roo.bootstrap.FieldLabel.register(this);
32754     },
32755     
32756     indicatorEl : function()
32757     {
32758         var indicator = this.el.select('i.roo-required-indicator',true).first();
32759         
32760         if(!indicator){
32761             return false;
32762         }
32763         
32764         return indicator;
32765         
32766     },
32767     
32768     /**
32769      * Mark this field as valid
32770      */
32771     markValid : function()
32772     {
32773         if(this.indicator){
32774             this.indicator.removeClass('visible');
32775             this.indicator.addClass('invisible');
32776         }
32777         if (Roo.bootstrap.version == 3) {
32778             this.el.removeClass(this.invalidClass);
32779             this.el.addClass(this.validClass);
32780         } else {
32781             this.el.removeClass('is-invalid');
32782             this.el.addClass('is-valid');
32783         }
32784         
32785         
32786         this.fireEvent('valid', this);
32787     },
32788     
32789     /**
32790      * Mark this field as invalid
32791      * @param {String} msg The validation message
32792      */
32793     markInvalid : function(msg)
32794     {
32795         if(this.indicator){
32796             this.indicator.removeClass('invisible');
32797             this.indicator.addClass('visible');
32798         }
32799           if (Roo.bootstrap.version == 3) {
32800             this.el.removeClass(this.validClass);
32801             this.el.addClass(this.invalidClass);
32802         } else {
32803             this.el.removeClass('is-valid');
32804             this.el.addClass('is-invalid');
32805         }
32806         
32807         
32808         this.fireEvent('invalid', this, msg);
32809     }
32810     
32811    
32812 });
32813
32814 Roo.apply(Roo.bootstrap.FieldLabel, {
32815     
32816     groups: {},
32817     
32818      /**
32819     * register a FieldLabel Group
32820     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32821     */
32822     register : function(label)
32823     {
32824         if(this.groups.hasOwnProperty(label.target)){
32825             return;
32826         }
32827      
32828         this.groups[label.target] = label;
32829         
32830     },
32831     /**
32832     * fetch a FieldLabel Group based on the target
32833     * @param {string} target
32834     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32835     */
32836     get: function(target) {
32837         if (typeof(this.groups[target]) == 'undefined') {
32838             return false;
32839         }
32840         
32841         return this.groups[target] ;
32842     }
32843 });
32844
32845  
32846
32847  /*
32848  * - LGPL
32849  *
32850  * page DateSplitField.
32851  * 
32852  */
32853
32854
32855 /**
32856  * @class Roo.bootstrap.DateSplitField
32857  * @extends Roo.bootstrap.Component
32858  * Bootstrap DateSplitField class
32859  * @cfg {string} fieldLabel - the label associated
32860  * @cfg {Number} labelWidth set the width of label (0-12)
32861  * @cfg {String} labelAlign (top|left)
32862  * @cfg {Boolean} dayAllowBlank (true|false) default false
32863  * @cfg {Boolean} monthAllowBlank (true|false) default false
32864  * @cfg {Boolean} yearAllowBlank (true|false) default false
32865  * @cfg {string} dayPlaceholder 
32866  * @cfg {string} monthPlaceholder
32867  * @cfg {string} yearPlaceholder
32868  * @cfg {string} dayFormat default 'd'
32869  * @cfg {string} monthFormat default 'm'
32870  * @cfg {string} yearFormat default 'Y'
32871  * @cfg {Number} labellg set the width of label (1-12)
32872  * @cfg {Number} labelmd set the width of label (1-12)
32873  * @cfg {Number} labelsm set the width of label (1-12)
32874  * @cfg {Number} labelxs set the width of label (1-12)
32875
32876  *     
32877  * @constructor
32878  * Create a new DateSplitField
32879  * @param {Object} config The config object
32880  */
32881
32882 Roo.bootstrap.DateSplitField = function(config){
32883     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32884     
32885     this.addEvents({
32886         // raw events
32887          /**
32888          * @event years
32889          * getting the data of years
32890          * @param {Roo.bootstrap.DateSplitField} this
32891          * @param {Object} years
32892          */
32893         "years" : true,
32894         /**
32895          * @event days
32896          * getting the data of days
32897          * @param {Roo.bootstrap.DateSplitField} this
32898          * @param {Object} days
32899          */
32900         "days" : true,
32901         /**
32902          * @event invalid
32903          * Fires after the field has been marked as invalid.
32904          * @param {Roo.form.Field} this
32905          * @param {String} msg The validation message
32906          */
32907         invalid : true,
32908        /**
32909          * @event valid
32910          * Fires after the field has been validated with no errors.
32911          * @param {Roo.form.Field} this
32912          */
32913         valid : true
32914     });
32915 };
32916
32917 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32918     
32919     fieldLabel : '',
32920     labelAlign : 'top',
32921     labelWidth : 3,
32922     dayAllowBlank : false,
32923     monthAllowBlank : false,
32924     yearAllowBlank : false,
32925     dayPlaceholder : '',
32926     monthPlaceholder : '',
32927     yearPlaceholder : '',
32928     dayFormat : 'd',
32929     monthFormat : 'm',
32930     yearFormat : 'Y',
32931     isFormField : true,
32932     labellg : 0,
32933     labelmd : 0,
32934     labelsm : 0,
32935     labelxs : 0,
32936     
32937     getAutoCreate : function()
32938     {
32939         var cfg = {
32940             tag : 'div',
32941             cls : 'row roo-date-split-field-group',
32942             cn : [
32943                 {
32944                     tag : 'input',
32945                     type : 'hidden',
32946                     cls : 'form-hidden-field roo-date-split-field-group-value',
32947                     name : this.name
32948                 }
32949             ]
32950         };
32951         
32952         var labelCls = 'col-md-12';
32953         var contentCls = 'col-md-4';
32954         
32955         if(this.fieldLabel){
32956             
32957             var label = {
32958                 tag : 'div',
32959                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32960                 cn : [
32961                     {
32962                         tag : 'label',
32963                         html : this.fieldLabel
32964                     }
32965                 ]
32966             };
32967             
32968             if(this.labelAlign == 'left'){
32969             
32970                 if(this.labelWidth > 12){
32971                     label.style = "width: " + this.labelWidth + 'px';
32972                 }
32973
32974                 if(this.labelWidth < 13 && this.labelmd == 0){
32975                     this.labelmd = this.labelWidth;
32976                 }
32977
32978                 if(this.labellg > 0){
32979                     labelCls = ' col-lg-' + this.labellg;
32980                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32981                 }
32982
32983                 if(this.labelmd > 0){
32984                     labelCls = ' col-md-' + this.labelmd;
32985                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32986                 }
32987
32988                 if(this.labelsm > 0){
32989                     labelCls = ' col-sm-' + this.labelsm;
32990                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32991                 }
32992
32993                 if(this.labelxs > 0){
32994                     labelCls = ' col-xs-' + this.labelxs;
32995                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32996                 }
32997             }
32998             
32999             label.cls += ' ' + labelCls;
33000             
33001             cfg.cn.push(label);
33002         }
33003         
33004         Roo.each(['day', 'month', 'year'], function(t){
33005             cfg.cn.push({
33006                 tag : 'div',
33007                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33008             });
33009         }, this);
33010         
33011         return cfg;
33012     },
33013     
33014     inputEl: function ()
33015     {
33016         return this.el.select('.roo-date-split-field-group-value', true).first();
33017     },
33018     
33019     onRender : function(ct, position) 
33020     {
33021         var _this = this;
33022         
33023         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33024         
33025         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33026         
33027         this.dayField = new Roo.bootstrap.ComboBox({
33028             allowBlank : this.dayAllowBlank,
33029             alwaysQuery : true,
33030             displayField : 'value',
33031             editable : false,
33032             fieldLabel : '',
33033             forceSelection : true,
33034             mode : 'local',
33035             placeholder : this.dayPlaceholder,
33036             selectOnFocus : true,
33037             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33038             triggerAction : 'all',
33039             typeAhead : true,
33040             valueField : 'value',
33041             store : new Roo.data.SimpleStore({
33042                 data : (function() {    
33043                     var days = [];
33044                     _this.fireEvent('days', _this, days);
33045                     return days;
33046                 })(),
33047                 fields : [ 'value' ]
33048             }),
33049             listeners : {
33050                 select : function (_self, record, index)
33051                 {
33052                     _this.setValue(_this.getValue());
33053                 }
33054             }
33055         });
33056
33057         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33058         
33059         this.monthField = new Roo.bootstrap.MonthField({
33060             after : '<i class=\"fa fa-calendar\"></i>',
33061             allowBlank : this.monthAllowBlank,
33062             placeholder : this.monthPlaceholder,
33063             readOnly : true,
33064             listeners : {
33065                 render : function (_self)
33066                 {
33067                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33068                         e.preventDefault();
33069                         _self.focus();
33070                     });
33071                 },
33072                 select : function (_self, oldvalue, newvalue)
33073                 {
33074                     _this.setValue(_this.getValue());
33075                 }
33076             }
33077         });
33078         
33079         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33080         
33081         this.yearField = new Roo.bootstrap.ComboBox({
33082             allowBlank : this.yearAllowBlank,
33083             alwaysQuery : true,
33084             displayField : 'value',
33085             editable : false,
33086             fieldLabel : '',
33087             forceSelection : true,
33088             mode : 'local',
33089             placeholder : this.yearPlaceholder,
33090             selectOnFocus : true,
33091             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33092             triggerAction : 'all',
33093             typeAhead : true,
33094             valueField : 'value',
33095             store : new Roo.data.SimpleStore({
33096                 data : (function() {
33097                     var years = [];
33098                     _this.fireEvent('years', _this, years);
33099                     return years;
33100                 })(),
33101                 fields : [ 'value' ]
33102             }),
33103             listeners : {
33104                 select : function (_self, record, index)
33105                 {
33106                     _this.setValue(_this.getValue());
33107                 }
33108             }
33109         });
33110
33111         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33112     },
33113     
33114     setValue : function(v, format)
33115     {
33116         this.inputEl.dom.value = v;
33117         
33118         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33119         
33120         var d = Date.parseDate(v, f);
33121         
33122         if(!d){
33123             this.validate();
33124             return;
33125         }
33126         
33127         this.setDay(d.format(this.dayFormat));
33128         this.setMonth(d.format(this.monthFormat));
33129         this.setYear(d.format(this.yearFormat));
33130         
33131         this.validate();
33132         
33133         return;
33134     },
33135     
33136     setDay : function(v)
33137     {
33138         this.dayField.setValue(v);
33139         this.inputEl.dom.value = this.getValue();
33140         this.validate();
33141         return;
33142     },
33143     
33144     setMonth : function(v)
33145     {
33146         this.monthField.setValue(v, true);
33147         this.inputEl.dom.value = this.getValue();
33148         this.validate();
33149         return;
33150     },
33151     
33152     setYear : function(v)
33153     {
33154         this.yearField.setValue(v);
33155         this.inputEl.dom.value = this.getValue();
33156         this.validate();
33157         return;
33158     },
33159     
33160     getDay : function()
33161     {
33162         return this.dayField.getValue();
33163     },
33164     
33165     getMonth : function()
33166     {
33167         return this.monthField.getValue();
33168     },
33169     
33170     getYear : function()
33171     {
33172         return this.yearField.getValue();
33173     },
33174     
33175     getValue : function()
33176     {
33177         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33178         
33179         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33180         
33181         return date;
33182     },
33183     
33184     reset : function()
33185     {
33186         this.setDay('');
33187         this.setMonth('');
33188         this.setYear('');
33189         this.inputEl.dom.value = '';
33190         this.validate();
33191         return;
33192     },
33193     
33194     validate : function()
33195     {
33196         var d = this.dayField.validate();
33197         var m = this.monthField.validate();
33198         var y = this.yearField.validate();
33199         
33200         var valid = true;
33201         
33202         if(
33203                 (!this.dayAllowBlank && !d) ||
33204                 (!this.monthAllowBlank && !m) ||
33205                 (!this.yearAllowBlank && !y)
33206         ){
33207             valid = false;
33208         }
33209         
33210         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33211             return valid;
33212         }
33213         
33214         if(valid){
33215             this.markValid();
33216             return valid;
33217         }
33218         
33219         this.markInvalid();
33220         
33221         return valid;
33222     },
33223     
33224     markValid : function()
33225     {
33226         
33227         var label = this.el.select('label', true).first();
33228         var icon = this.el.select('i.fa-star', true).first();
33229
33230         if(label && icon){
33231             icon.remove();
33232         }
33233         
33234         this.fireEvent('valid', this);
33235     },
33236     
33237      /**
33238      * Mark this field as invalid
33239      * @param {String} msg The validation message
33240      */
33241     markInvalid : function(msg)
33242     {
33243         
33244         var label = this.el.select('label', true).first();
33245         var icon = this.el.select('i.fa-star', true).first();
33246
33247         if(label && !icon){
33248             this.el.select('.roo-date-split-field-label', true).createChild({
33249                 tag : 'i',
33250                 cls : 'text-danger fa fa-lg fa-star',
33251                 tooltip : 'This field is required',
33252                 style : 'margin-right:5px;'
33253             }, label, true);
33254         }
33255         
33256         this.fireEvent('invalid', this, msg);
33257     },
33258     
33259     clearInvalid : function()
33260     {
33261         var label = this.el.select('label', true).first();
33262         var icon = this.el.select('i.fa-star', true).first();
33263
33264         if(label && icon){
33265             icon.remove();
33266         }
33267         
33268         this.fireEvent('valid', this);
33269     },
33270     
33271     getName: function()
33272     {
33273         return this.name;
33274     }
33275     
33276 });
33277
33278  /**
33279  *
33280  * This is based on 
33281  * http://masonry.desandro.com
33282  *
33283  * The idea is to render all the bricks based on vertical width...
33284  *
33285  * The original code extends 'outlayer' - we might need to use that....
33286  * 
33287  */
33288
33289
33290 /**
33291  * @class Roo.bootstrap.LayoutMasonry
33292  * @extends Roo.bootstrap.Component
33293  * Bootstrap Layout Masonry class
33294  * 
33295  * @constructor
33296  * Create a new Element
33297  * @param {Object} config The config object
33298  */
33299
33300 Roo.bootstrap.LayoutMasonry = function(config){
33301     
33302     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33303     
33304     this.bricks = [];
33305     
33306     Roo.bootstrap.LayoutMasonry.register(this);
33307     
33308     this.addEvents({
33309         // raw events
33310         /**
33311          * @event layout
33312          * Fire after layout the items
33313          * @param {Roo.bootstrap.LayoutMasonry} this
33314          * @param {Roo.EventObject} e
33315          */
33316         "layout" : true
33317     });
33318     
33319 };
33320
33321 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33322     
33323     /**
33324      * @cfg {Boolean} isLayoutInstant = no animation?
33325      */   
33326     isLayoutInstant : false, // needed?
33327    
33328     /**
33329      * @cfg {Number} boxWidth  width of the columns
33330      */   
33331     boxWidth : 450,
33332     
33333       /**
33334      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33335      */   
33336     boxHeight : 0,
33337     
33338     /**
33339      * @cfg {Number} padWidth padding below box..
33340      */   
33341     padWidth : 10, 
33342     
33343     /**
33344      * @cfg {Number} gutter gutter width..
33345      */   
33346     gutter : 10,
33347     
33348      /**
33349      * @cfg {Number} maxCols maximum number of columns
33350      */   
33351     
33352     maxCols: 0,
33353     
33354     /**
33355      * @cfg {Boolean} isAutoInitial defalut true
33356      */   
33357     isAutoInitial : true, 
33358     
33359     containerWidth: 0,
33360     
33361     /**
33362      * @cfg {Boolean} isHorizontal defalut false
33363      */   
33364     isHorizontal : false, 
33365
33366     currentSize : null,
33367     
33368     tag: 'div',
33369     
33370     cls: '',
33371     
33372     bricks: null, //CompositeElement
33373     
33374     cols : 1,
33375     
33376     _isLayoutInited : false,
33377     
33378 //    isAlternative : false, // only use for vertical layout...
33379     
33380     /**
33381      * @cfg {Number} alternativePadWidth padding below box..
33382      */   
33383     alternativePadWidth : 50,
33384     
33385     selectedBrick : [],
33386     
33387     getAutoCreate : function(){
33388         
33389         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33390         
33391         var cfg = {
33392             tag: this.tag,
33393             cls: 'blog-masonary-wrapper ' + this.cls,
33394             cn : {
33395                 cls : 'mas-boxes masonary'
33396             }
33397         };
33398         
33399         return cfg;
33400     },
33401     
33402     getChildContainer: function( )
33403     {
33404         if (this.boxesEl) {
33405             return this.boxesEl;
33406         }
33407         
33408         this.boxesEl = this.el.select('.mas-boxes').first();
33409         
33410         return this.boxesEl;
33411     },
33412     
33413     
33414     initEvents : function()
33415     {
33416         var _this = this;
33417         
33418         if(this.isAutoInitial){
33419             Roo.log('hook children rendered');
33420             this.on('childrenrendered', function() {
33421                 Roo.log('children rendered');
33422                 _this.initial();
33423             } ,this);
33424         }
33425     },
33426     
33427     initial : function()
33428     {
33429         this.selectedBrick = [];
33430         
33431         this.currentSize = this.el.getBox(true);
33432         
33433         Roo.EventManager.onWindowResize(this.resize, this); 
33434
33435         if(!this.isAutoInitial){
33436             this.layout();
33437             return;
33438         }
33439         
33440         this.layout();
33441         
33442         return;
33443         //this.layout.defer(500,this);
33444         
33445     },
33446     
33447     resize : function()
33448     {
33449         var cs = this.el.getBox(true);
33450         
33451         if (
33452                 this.currentSize.width == cs.width && 
33453                 this.currentSize.x == cs.x && 
33454                 this.currentSize.height == cs.height && 
33455                 this.currentSize.y == cs.y 
33456         ) {
33457             Roo.log("no change in with or X or Y");
33458             return;
33459         }
33460         
33461         this.currentSize = cs;
33462         
33463         this.layout();
33464         
33465     },
33466     
33467     layout : function()
33468     {   
33469         this._resetLayout();
33470         
33471         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33472         
33473         this.layoutItems( isInstant );
33474       
33475         this._isLayoutInited = true;
33476         
33477         this.fireEvent('layout', this);
33478         
33479     },
33480     
33481     _resetLayout : function()
33482     {
33483         if(this.isHorizontal){
33484             this.horizontalMeasureColumns();
33485             return;
33486         }
33487         
33488         this.verticalMeasureColumns();
33489         
33490     },
33491     
33492     verticalMeasureColumns : function()
33493     {
33494         this.getContainerWidth();
33495         
33496 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33497 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33498 //            return;
33499 //        }
33500         
33501         var boxWidth = this.boxWidth + this.padWidth;
33502         
33503         if(this.containerWidth < this.boxWidth){
33504             boxWidth = this.containerWidth
33505         }
33506         
33507         var containerWidth = this.containerWidth;
33508         
33509         var cols = Math.floor(containerWidth / boxWidth);
33510         
33511         this.cols = Math.max( cols, 1 );
33512         
33513         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33514         
33515         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33516         
33517         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33518         
33519         this.colWidth = boxWidth + avail - this.padWidth;
33520         
33521         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33522         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33523     },
33524     
33525     horizontalMeasureColumns : function()
33526     {
33527         this.getContainerWidth();
33528         
33529         var boxWidth = this.boxWidth;
33530         
33531         if(this.containerWidth < boxWidth){
33532             boxWidth = this.containerWidth;
33533         }
33534         
33535         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33536         
33537         this.el.setHeight(boxWidth);
33538         
33539     },
33540     
33541     getContainerWidth : function()
33542     {
33543         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33544     },
33545     
33546     layoutItems : function( isInstant )
33547     {
33548         Roo.log(this.bricks);
33549         
33550         var items = Roo.apply([], this.bricks);
33551         
33552         if(this.isHorizontal){
33553             this._horizontalLayoutItems( items , isInstant );
33554             return;
33555         }
33556         
33557 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33558 //            this._verticalAlternativeLayoutItems( items , isInstant );
33559 //            return;
33560 //        }
33561         
33562         this._verticalLayoutItems( items , isInstant );
33563         
33564     },
33565     
33566     _verticalLayoutItems : function ( items , isInstant)
33567     {
33568         if ( !items || !items.length ) {
33569             return;
33570         }
33571         
33572         var standard = [
33573             ['xs', 'xs', 'xs', 'tall'],
33574             ['xs', 'xs', 'tall'],
33575             ['xs', 'xs', 'sm'],
33576             ['xs', 'xs', 'xs'],
33577             ['xs', 'tall'],
33578             ['xs', 'sm'],
33579             ['xs', 'xs'],
33580             ['xs'],
33581             
33582             ['sm', 'xs', 'xs'],
33583             ['sm', 'xs'],
33584             ['sm'],
33585             
33586             ['tall', 'xs', 'xs', 'xs'],
33587             ['tall', 'xs', 'xs'],
33588             ['tall', 'xs'],
33589             ['tall']
33590             
33591         ];
33592         
33593         var queue = [];
33594         
33595         var boxes = [];
33596         
33597         var box = [];
33598         
33599         Roo.each(items, function(item, k){
33600             
33601             switch (item.size) {
33602                 // these layouts take up a full box,
33603                 case 'md' :
33604                 case 'md-left' :
33605                 case 'md-right' :
33606                 case 'wide' :
33607                     
33608                     if(box.length){
33609                         boxes.push(box);
33610                         box = [];
33611                     }
33612                     
33613                     boxes.push([item]);
33614                     
33615                     break;
33616                     
33617                 case 'xs' :
33618                 case 'sm' :
33619                 case 'tall' :
33620                     
33621                     box.push(item);
33622                     
33623                     break;
33624                 default :
33625                     break;
33626                     
33627             }
33628             
33629         }, this);
33630         
33631         if(box.length){
33632             boxes.push(box);
33633             box = [];
33634         }
33635         
33636         var filterPattern = function(box, length)
33637         {
33638             if(!box.length){
33639                 return;
33640             }
33641             
33642             var match = false;
33643             
33644             var pattern = box.slice(0, length);
33645             
33646             var format = [];
33647             
33648             Roo.each(pattern, function(i){
33649                 format.push(i.size);
33650             }, this);
33651             
33652             Roo.each(standard, function(s){
33653                 
33654                 if(String(s) != String(format)){
33655                     return;
33656                 }
33657                 
33658                 match = true;
33659                 return false;
33660                 
33661             }, this);
33662             
33663             if(!match && length == 1){
33664                 return;
33665             }
33666             
33667             if(!match){
33668                 filterPattern(box, length - 1);
33669                 return;
33670             }
33671                 
33672             queue.push(pattern);
33673
33674             box = box.slice(length, box.length);
33675
33676             filterPattern(box, 4);
33677
33678             return;
33679             
33680         }
33681         
33682         Roo.each(boxes, function(box, k){
33683             
33684             if(!box.length){
33685                 return;
33686             }
33687             
33688             if(box.length == 1){
33689                 queue.push(box);
33690                 return;
33691             }
33692             
33693             filterPattern(box, 4);
33694             
33695         }, this);
33696         
33697         this._processVerticalLayoutQueue( queue, isInstant );
33698         
33699     },
33700     
33701 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33702 //    {
33703 //        if ( !items || !items.length ) {
33704 //            return;
33705 //        }
33706 //
33707 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33708 //        
33709 //    },
33710     
33711     _horizontalLayoutItems : function ( items , isInstant)
33712     {
33713         if ( !items || !items.length || items.length < 3) {
33714             return;
33715         }
33716         
33717         items.reverse();
33718         
33719         var eItems = items.slice(0, 3);
33720         
33721         items = items.slice(3, items.length);
33722         
33723         var standard = [
33724             ['xs', 'xs', 'xs', 'wide'],
33725             ['xs', 'xs', 'wide'],
33726             ['xs', 'xs', 'sm'],
33727             ['xs', 'xs', 'xs'],
33728             ['xs', 'wide'],
33729             ['xs', 'sm'],
33730             ['xs', 'xs'],
33731             ['xs'],
33732             
33733             ['sm', 'xs', 'xs'],
33734             ['sm', 'xs'],
33735             ['sm'],
33736             
33737             ['wide', 'xs', 'xs', 'xs'],
33738             ['wide', 'xs', 'xs'],
33739             ['wide', 'xs'],
33740             ['wide'],
33741             
33742             ['wide-thin']
33743         ];
33744         
33745         var queue = [];
33746         
33747         var boxes = [];
33748         
33749         var box = [];
33750         
33751         Roo.each(items, function(item, k){
33752             
33753             switch (item.size) {
33754                 case 'md' :
33755                 case 'md-left' :
33756                 case 'md-right' :
33757                 case 'tall' :
33758                     
33759                     if(box.length){
33760                         boxes.push(box);
33761                         box = [];
33762                     }
33763                     
33764                     boxes.push([item]);
33765                     
33766                     break;
33767                     
33768                 case 'xs' :
33769                 case 'sm' :
33770                 case 'wide' :
33771                 case 'wide-thin' :
33772                     
33773                     box.push(item);
33774                     
33775                     break;
33776                 default :
33777                     break;
33778                     
33779             }
33780             
33781         }, this);
33782         
33783         if(box.length){
33784             boxes.push(box);
33785             box = [];
33786         }
33787         
33788         var filterPattern = function(box, length)
33789         {
33790             if(!box.length){
33791                 return;
33792             }
33793             
33794             var match = false;
33795             
33796             var pattern = box.slice(0, length);
33797             
33798             var format = [];
33799             
33800             Roo.each(pattern, function(i){
33801                 format.push(i.size);
33802             }, this);
33803             
33804             Roo.each(standard, function(s){
33805                 
33806                 if(String(s) != String(format)){
33807                     return;
33808                 }
33809                 
33810                 match = true;
33811                 return false;
33812                 
33813             }, this);
33814             
33815             if(!match && length == 1){
33816                 return;
33817             }
33818             
33819             if(!match){
33820                 filterPattern(box, length - 1);
33821                 return;
33822             }
33823                 
33824             queue.push(pattern);
33825
33826             box = box.slice(length, box.length);
33827
33828             filterPattern(box, 4);
33829
33830             return;
33831             
33832         }
33833         
33834         Roo.each(boxes, function(box, k){
33835             
33836             if(!box.length){
33837                 return;
33838             }
33839             
33840             if(box.length == 1){
33841                 queue.push(box);
33842                 return;
33843             }
33844             
33845             filterPattern(box, 4);
33846             
33847         }, this);
33848         
33849         
33850         var prune = [];
33851         
33852         var pos = this.el.getBox(true);
33853         
33854         var minX = pos.x;
33855         
33856         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33857         
33858         var hit_end = false;
33859         
33860         Roo.each(queue, function(box){
33861             
33862             if(hit_end){
33863                 
33864                 Roo.each(box, function(b){
33865                 
33866                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33867                     b.el.hide();
33868
33869                 }, this);
33870
33871                 return;
33872             }
33873             
33874             var mx = 0;
33875             
33876             Roo.each(box, function(b){
33877                 
33878                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33879                 b.el.show();
33880
33881                 mx = Math.max(mx, b.x);
33882                 
33883             }, this);
33884             
33885             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33886             
33887             if(maxX < minX){
33888                 
33889                 Roo.each(box, function(b){
33890                 
33891                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33892                     b.el.hide();
33893                     
33894                 }, this);
33895                 
33896                 hit_end = true;
33897                 
33898                 return;
33899             }
33900             
33901             prune.push(box);
33902             
33903         }, this);
33904         
33905         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33906     },
33907     
33908     /** Sets position of item in DOM
33909     * @param {Element} item
33910     * @param {Number} x - horizontal position
33911     * @param {Number} y - vertical position
33912     * @param {Boolean} isInstant - disables transitions
33913     */
33914     _processVerticalLayoutQueue : function( queue, isInstant )
33915     {
33916         var pos = this.el.getBox(true);
33917         var x = pos.x;
33918         var y = pos.y;
33919         var maxY = [];
33920         
33921         for (var i = 0; i < this.cols; i++){
33922             maxY[i] = pos.y;
33923         }
33924         
33925         Roo.each(queue, function(box, k){
33926             
33927             var col = k % this.cols;
33928             
33929             Roo.each(box, function(b,kk){
33930                 
33931                 b.el.position('absolute');
33932                 
33933                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33934                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33935                 
33936                 if(b.size == 'md-left' || b.size == 'md-right'){
33937                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33938                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33939                 }
33940                 
33941                 b.el.setWidth(width);
33942                 b.el.setHeight(height);
33943                 // iframe?
33944                 b.el.select('iframe',true).setSize(width,height);
33945                 
33946             }, this);
33947             
33948             for (var i = 0; i < this.cols; i++){
33949                 
33950                 if(maxY[i] < maxY[col]){
33951                     col = i;
33952                     continue;
33953                 }
33954                 
33955                 col = Math.min(col, i);
33956                 
33957             }
33958             
33959             x = pos.x + col * (this.colWidth + this.padWidth);
33960             
33961             y = maxY[col];
33962             
33963             var positions = [];
33964             
33965             switch (box.length){
33966                 case 1 :
33967                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33968                     break;
33969                 case 2 :
33970                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33971                     break;
33972                 case 3 :
33973                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33974                     break;
33975                 case 4 :
33976                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33977                     break;
33978                 default :
33979                     break;
33980             }
33981             
33982             Roo.each(box, function(b,kk){
33983                 
33984                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33985                 
33986                 var sz = b.el.getSize();
33987                 
33988                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33989                 
33990             }, this);
33991             
33992         }, this);
33993         
33994         var mY = 0;
33995         
33996         for (var i = 0; i < this.cols; i++){
33997             mY = Math.max(mY, maxY[i]);
33998         }
33999         
34000         this.el.setHeight(mY - pos.y);
34001         
34002     },
34003     
34004 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34005 //    {
34006 //        var pos = this.el.getBox(true);
34007 //        var x = pos.x;
34008 //        var y = pos.y;
34009 //        var maxX = pos.right;
34010 //        
34011 //        var maxHeight = 0;
34012 //        
34013 //        Roo.each(items, function(item, k){
34014 //            
34015 //            var c = k % 2;
34016 //            
34017 //            item.el.position('absolute');
34018 //                
34019 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34020 //
34021 //            item.el.setWidth(width);
34022 //
34023 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34024 //
34025 //            item.el.setHeight(height);
34026 //            
34027 //            if(c == 0){
34028 //                item.el.setXY([x, y], isInstant ? false : true);
34029 //            } else {
34030 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34031 //            }
34032 //            
34033 //            y = y + height + this.alternativePadWidth;
34034 //            
34035 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34036 //            
34037 //        }, this);
34038 //        
34039 //        this.el.setHeight(maxHeight);
34040 //        
34041 //    },
34042     
34043     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34044     {
34045         var pos = this.el.getBox(true);
34046         
34047         var minX = pos.x;
34048         var minY = pos.y;
34049         
34050         var maxX = pos.right;
34051         
34052         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34053         
34054         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34055         
34056         Roo.each(queue, function(box, k){
34057             
34058             Roo.each(box, function(b, kk){
34059                 
34060                 b.el.position('absolute');
34061                 
34062                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34063                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34064                 
34065                 if(b.size == 'md-left' || b.size == 'md-right'){
34066                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34067                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34068                 }
34069                 
34070                 b.el.setWidth(width);
34071                 b.el.setHeight(height);
34072                 
34073             }, this);
34074             
34075             if(!box.length){
34076                 return;
34077             }
34078             
34079             var positions = [];
34080             
34081             switch (box.length){
34082                 case 1 :
34083                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34084                     break;
34085                 case 2 :
34086                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34087                     break;
34088                 case 3 :
34089                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34090                     break;
34091                 case 4 :
34092                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34093                     break;
34094                 default :
34095                     break;
34096             }
34097             
34098             Roo.each(box, function(b,kk){
34099                 
34100                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34101                 
34102                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34103                 
34104             }, this);
34105             
34106         }, this);
34107         
34108     },
34109     
34110     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34111     {
34112         Roo.each(eItems, function(b,k){
34113             
34114             b.size = (k == 0) ? 'sm' : 'xs';
34115             b.x = (k == 0) ? 2 : 1;
34116             b.y = (k == 0) ? 2 : 1;
34117             
34118             b.el.position('absolute');
34119             
34120             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34121                 
34122             b.el.setWidth(width);
34123             
34124             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34125             
34126             b.el.setHeight(height);
34127             
34128         }, this);
34129
34130         var positions = [];
34131         
34132         positions.push({
34133             x : maxX - this.unitWidth * 2 - this.gutter,
34134             y : minY
34135         });
34136         
34137         positions.push({
34138             x : maxX - this.unitWidth,
34139             y : minY + (this.unitWidth + this.gutter) * 2
34140         });
34141         
34142         positions.push({
34143             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34144             y : minY
34145         });
34146         
34147         Roo.each(eItems, function(b,k){
34148             
34149             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34150
34151         }, this);
34152         
34153     },
34154     
34155     getVerticalOneBoxColPositions : function(x, y, box)
34156     {
34157         var pos = [];
34158         
34159         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34160         
34161         if(box[0].size == 'md-left'){
34162             rand = 0;
34163         }
34164         
34165         if(box[0].size == 'md-right'){
34166             rand = 1;
34167         }
34168         
34169         pos.push({
34170             x : x + (this.unitWidth + this.gutter) * rand,
34171             y : y
34172         });
34173         
34174         return pos;
34175     },
34176     
34177     getVerticalTwoBoxColPositions : function(x, y, box)
34178     {
34179         var pos = [];
34180         
34181         if(box[0].size == 'xs'){
34182             
34183             pos.push({
34184                 x : x,
34185                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34186             });
34187
34188             pos.push({
34189                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34190                 y : y
34191             });
34192             
34193             return pos;
34194             
34195         }
34196         
34197         pos.push({
34198             x : x,
34199             y : y
34200         });
34201
34202         pos.push({
34203             x : x + (this.unitWidth + this.gutter) * 2,
34204             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34205         });
34206         
34207         return pos;
34208         
34209     },
34210     
34211     getVerticalThreeBoxColPositions : function(x, y, box)
34212     {
34213         var pos = [];
34214         
34215         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34216             
34217             pos.push({
34218                 x : x,
34219                 y : y
34220             });
34221
34222             pos.push({
34223                 x : x + (this.unitWidth + this.gutter) * 1,
34224                 y : y
34225             });
34226             
34227             pos.push({
34228                 x : x + (this.unitWidth + this.gutter) * 2,
34229                 y : y
34230             });
34231             
34232             return pos;
34233             
34234         }
34235         
34236         if(box[0].size == 'xs' && box[1].size == 'xs'){
34237             
34238             pos.push({
34239                 x : x,
34240                 y : y
34241             });
34242
34243             pos.push({
34244                 x : x,
34245                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34246             });
34247             
34248             pos.push({
34249                 x : x + (this.unitWidth + this.gutter) * 1,
34250                 y : y
34251             });
34252             
34253             return pos;
34254             
34255         }
34256         
34257         pos.push({
34258             x : x,
34259             y : y
34260         });
34261
34262         pos.push({
34263             x : x + (this.unitWidth + this.gutter) * 2,
34264             y : y
34265         });
34266
34267         pos.push({
34268             x : x + (this.unitWidth + this.gutter) * 2,
34269             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34270         });
34271             
34272         return pos;
34273         
34274     },
34275     
34276     getVerticalFourBoxColPositions : function(x, y, box)
34277     {
34278         var pos = [];
34279         
34280         if(box[0].size == 'xs'){
34281             
34282             pos.push({
34283                 x : x,
34284                 y : y
34285             });
34286
34287             pos.push({
34288                 x : x,
34289                 y : y + (this.unitHeight + this.gutter) * 1
34290             });
34291             
34292             pos.push({
34293                 x : x,
34294                 y : y + (this.unitHeight + this.gutter) * 2
34295             });
34296             
34297             pos.push({
34298                 x : x + (this.unitWidth + this.gutter) * 1,
34299                 y : y
34300             });
34301             
34302             return pos;
34303             
34304         }
34305         
34306         pos.push({
34307             x : x,
34308             y : y
34309         });
34310
34311         pos.push({
34312             x : x + (this.unitWidth + this.gutter) * 2,
34313             y : y
34314         });
34315
34316         pos.push({
34317             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34318             y : y + (this.unitHeight + this.gutter) * 1
34319         });
34320
34321         pos.push({
34322             x : x + (this.unitWidth + this.gutter) * 2,
34323             y : y + (this.unitWidth + this.gutter) * 2
34324         });
34325
34326         return pos;
34327         
34328     },
34329     
34330     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34331     {
34332         var pos = [];
34333         
34334         if(box[0].size == 'md-left'){
34335             pos.push({
34336                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34337                 y : minY
34338             });
34339             
34340             return pos;
34341         }
34342         
34343         if(box[0].size == 'md-right'){
34344             pos.push({
34345                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34346                 y : minY + (this.unitWidth + this.gutter) * 1
34347             });
34348             
34349             return pos;
34350         }
34351         
34352         var rand = Math.floor(Math.random() * (4 - box[0].y));
34353         
34354         pos.push({
34355             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34356             y : minY + (this.unitWidth + this.gutter) * rand
34357         });
34358         
34359         return pos;
34360         
34361     },
34362     
34363     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34364     {
34365         var pos = [];
34366         
34367         if(box[0].size == 'xs'){
34368             
34369             pos.push({
34370                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34371                 y : minY
34372             });
34373
34374             pos.push({
34375                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34376                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34377             });
34378             
34379             return pos;
34380             
34381         }
34382         
34383         pos.push({
34384             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34385             y : minY
34386         });
34387
34388         pos.push({
34389             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34390             y : minY + (this.unitWidth + this.gutter) * 2
34391         });
34392         
34393         return pos;
34394         
34395     },
34396     
34397     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34398     {
34399         var pos = [];
34400         
34401         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34402             
34403             pos.push({
34404                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34405                 y : minY
34406             });
34407
34408             pos.push({
34409                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34410                 y : minY + (this.unitWidth + this.gutter) * 1
34411             });
34412             
34413             pos.push({
34414                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34415                 y : minY + (this.unitWidth + this.gutter) * 2
34416             });
34417             
34418             return pos;
34419             
34420         }
34421         
34422         if(box[0].size == 'xs' && box[1].size == 'xs'){
34423             
34424             pos.push({
34425                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34426                 y : minY
34427             });
34428
34429             pos.push({
34430                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34431                 y : minY
34432             });
34433             
34434             pos.push({
34435                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34436                 y : minY + (this.unitWidth + this.gutter) * 1
34437             });
34438             
34439             return pos;
34440             
34441         }
34442         
34443         pos.push({
34444             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34445             y : minY
34446         });
34447
34448         pos.push({
34449             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34450             y : minY + (this.unitWidth + this.gutter) * 2
34451         });
34452
34453         pos.push({
34454             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34455             y : minY + (this.unitWidth + this.gutter) * 2
34456         });
34457             
34458         return pos;
34459         
34460     },
34461     
34462     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34463     {
34464         var pos = [];
34465         
34466         if(box[0].size == 'xs'){
34467             
34468             pos.push({
34469                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34470                 y : minY
34471             });
34472
34473             pos.push({
34474                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34475                 y : minY
34476             });
34477             
34478             pos.push({
34479                 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),
34480                 y : minY
34481             });
34482             
34483             pos.push({
34484                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34485                 y : minY + (this.unitWidth + this.gutter) * 1
34486             });
34487             
34488             return pos;
34489             
34490         }
34491         
34492         pos.push({
34493             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34494             y : minY
34495         });
34496         
34497         pos.push({
34498             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34499             y : minY + (this.unitWidth + this.gutter) * 2
34500         });
34501         
34502         pos.push({
34503             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34504             y : minY + (this.unitWidth + this.gutter) * 2
34505         });
34506         
34507         pos.push({
34508             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),
34509             y : minY + (this.unitWidth + this.gutter) * 2
34510         });
34511
34512         return pos;
34513         
34514     },
34515     
34516     /**
34517     * remove a Masonry Brick
34518     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34519     */
34520     removeBrick : function(brick_id)
34521     {
34522         if (!brick_id) {
34523             return;
34524         }
34525         
34526         for (var i = 0; i<this.bricks.length; i++) {
34527             if (this.bricks[i].id == brick_id) {
34528                 this.bricks.splice(i,1);
34529                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34530                 this.initial();
34531             }
34532         }
34533     },
34534     
34535     /**
34536     * adds a Masonry Brick
34537     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34538     */
34539     addBrick : function(cfg)
34540     {
34541         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34542         //this.register(cn);
34543         cn.parentId = this.id;
34544         cn.render(this.el);
34545         return cn;
34546     },
34547     
34548     /**
34549     * register a Masonry Brick
34550     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34551     */
34552     
34553     register : function(brick)
34554     {
34555         this.bricks.push(brick);
34556         brick.masonryId = this.id;
34557     },
34558     
34559     /**
34560     * clear all the Masonry Brick
34561     */
34562     clearAll : function()
34563     {
34564         this.bricks = [];
34565         //this.getChildContainer().dom.innerHTML = "";
34566         this.el.dom.innerHTML = '';
34567     },
34568     
34569     getSelected : function()
34570     {
34571         if (!this.selectedBrick) {
34572             return false;
34573         }
34574         
34575         return this.selectedBrick;
34576     }
34577 });
34578
34579 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34580     
34581     groups: {},
34582      /**
34583     * register a Masonry Layout
34584     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34585     */
34586     
34587     register : function(layout)
34588     {
34589         this.groups[layout.id] = layout;
34590     },
34591     /**
34592     * fetch a  Masonry Layout based on the masonry layout ID
34593     * @param {string} the masonry layout to add
34594     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34595     */
34596     
34597     get: function(layout_id) {
34598         if (typeof(this.groups[layout_id]) == 'undefined') {
34599             return false;
34600         }
34601         return this.groups[layout_id] ;
34602     }
34603     
34604     
34605     
34606 });
34607
34608  
34609
34610  /**
34611  *
34612  * This is based on 
34613  * http://masonry.desandro.com
34614  *
34615  * The idea is to render all the bricks based on vertical width...
34616  *
34617  * The original code extends 'outlayer' - we might need to use that....
34618  * 
34619  */
34620
34621
34622 /**
34623  * @class Roo.bootstrap.LayoutMasonryAuto
34624  * @extends Roo.bootstrap.Component
34625  * Bootstrap Layout Masonry class
34626  * 
34627  * @constructor
34628  * Create a new Element
34629  * @param {Object} config The config object
34630  */
34631
34632 Roo.bootstrap.LayoutMasonryAuto = function(config){
34633     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34634 };
34635
34636 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34637     
34638       /**
34639      * @cfg {Boolean} isFitWidth  - resize the width..
34640      */   
34641     isFitWidth : false,  // options..
34642     /**
34643      * @cfg {Boolean} isOriginLeft = left align?
34644      */   
34645     isOriginLeft : true,
34646     /**
34647      * @cfg {Boolean} isOriginTop = top align?
34648      */   
34649     isOriginTop : false,
34650     /**
34651      * @cfg {Boolean} isLayoutInstant = no animation?
34652      */   
34653     isLayoutInstant : false, // needed?
34654     /**
34655      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34656      */   
34657     isResizingContainer : true,
34658     /**
34659      * @cfg {Number} columnWidth  width of the columns 
34660      */   
34661     
34662     columnWidth : 0,
34663     
34664     /**
34665      * @cfg {Number} maxCols maximum number of columns
34666      */   
34667     
34668     maxCols: 0,
34669     /**
34670      * @cfg {Number} padHeight padding below box..
34671      */   
34672     
34673     padHeight : 10, 
34674     
34675     /**
34676      * @cfg {Boolean} isAutoInitial defalut true
34677      */   
34678     
34679     isAutoInitial : true, 
34680     
34681     // private?
34682     gutter : 0,
34683     
34684     containerWidth: 0,
34685     initialColumnWidth : 0,
34686     currentSize : null,
34687     
34688     colYs : null, // array.
34689     maxY : 0,
34690     padWidth: 10,
34691     
34692     
34693     tag: 'div',
34694     cls: '',
34695     bricks: null, //CompositeElement
34696     cols : 0, // array?
34697     // element : null, // wrapped now this.el
34698     _isLayoutInited : null, 
34699     
34700     
34701     getAutoCreate : function(){
34702         
34703         var cfg = {
34704             tag: this.tag,
34705             cls: 'blog-masonary-wrapper ' + this.cls,
34706             cn : {
34707                 cls : 'mas-boxes masonary'
34708             }
34709         };
34710         
34711         return cfg;
34712     },
34713     
34714     getChildContainer: function( )
34715     {
34716         if (this.boxesEl) {
34717             return this.boxesEl;
34718         }
34719         
34720         this.boxesEl = this.el.select('.mas-boxes').first();
34721         
34722         return this.boxesEl;
34723     },
34724     
34725     
34726     initEvents : function()
34727     {
34728         var _this = this;
34729         
34730         if(this.isAutoInitial){
34731             Roo.log('hook children rendered');
34732             this.on('childrenrendered', function() {
34733                 Roo.log('children rendered');
34734                 _this.initial();
34735             } ,this);
34736         }
34737         
34738     },
34739     
34740     initial : function()
34741     {
34742         this.reloadItems();
34743
34744         this.currentSize = this.el.getBox(true);
34745
34746         /// was window resize... - let's see if this works..
34747         Roo.EventManager.onWindowResize(this.resize, this); 
34748
34749         if(!this.isAutoInitial){
34750             this.layout();
34751             return;
34752         }
34753         
34754         this.layout.defer(500,this);
34755     },
34756     
34757     reloadItems: function()
34758     {
34759         this.bricks = this.el.select('.masonry-brick', true);
34760         
34761         this.bricks.each(function(b) {
34762             //Roo.log(b.getSize());
34763             if (!b.attr('originalwidth')) {
34764                 b.attr('originalwidth',  b.getSize().width);
34765             }
34766             
34767         });
34768         
34769         Roo.log(this.bricks.elements.length);
34770     },
34771     
34772     resize : function()
34773     {
34774         Roo.log('resize');
34775         var cs = this.el.getBox(true);
34776         
34777         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34778             Roo.log("no change in with or X");
34779             return;
34780         }
34781         this.currentSize = cs;
34782         this.layout();
34783     },
34784     
34785     layout : function()
34786     {
34787          Roo.log('layout');
34788         this._resetLayout();
34789         //this._manageStamps();
34790       
34791         // don't animate first layout
34792         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34793         this.layoutItems( isInstant );
34794       
34795         // flag for initalized
34796         this._isLayoutInited = true;
34797     },
34798     
34799     layoutItems : function( isInstant )
34800     {
34801         //var items = this._getItemsForLayout( this.items );
34802         // original code supports filtering layout items.. we just ignore it..
34803         
34804         this._layoutItems( this.bricks , isInstant );
34805       
34806         this._postLayout();
34807     },
34808     _layoutItems : function ( items , isInstant)
34809     {
34810        //this.fireEvent( 'layout', this, items );
34811     
34812
34813         if ( !items || !items.elements.length ) {
34814           // no items, emit event with empty array
34815             return;
34816         }
34817
34818         var queue = [];
34819         items.each(function(item) {
34820             Roo.log("layout item");
34821             Roo.log(item);
34822             // get x/y object from method
34823             var position = this._getItemLayoutPosition( item );
34824             // enqueue
34825             position.item = item;
34826             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34827             queue.push( position );
34828         }, this);
34829       
34830         this._processLayoutQueue( queue );
34831     },
34832     /** Sets position of item in DOM
34833     * @param {Element} item
34834     * @param {Number} x - horizontal position
34835     * @param {Number} y - vertical position
34836     * @param {Boolean} isInstant - disables transitions
34837     */
34838     _processLayoutQueue : function( queue )
34839     {
34840         for ( var i=0, len = queue.length; i < len; i++ ) {
34841             var obj = queue[i];
34842             obj.item.position('absolute');
34843             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34844         }
34845     },
34846       
34847     
34848     /**
34849     * Any logic you want to do after each layout,
34850     * i.e. size the container
34851     */
34852     _postLayout : function()
34853     {
34854         this.resizeContainer();
34855     },
34856     
34857     resizeContainer : function()
34858     {
34859         if ( !this.isResizingContainer ) {
34860             return;
34861         }
34862         var size = this._getContainerSize();
34863         if ( size ) {
34864             this.el.setSize(size.width,size.height);
34865             this.boxesEl.setSize(size.width,size.height);
34866         }
34867     },
34868     
34869     
34870     
34871     _resetLayout : function()
34872     {
34873         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34874         this.colWidth = this.el.getWidth();
34875         //this.gutter = this.el.getWidth(); 
34876         
34877         this.measureColumns();
34878
34879         // reset column Y
34880         var i = this.cols;
34881         this.colYs = [];
34882         while (i--) {
34883             this.colYs.push( 0 );
34884         }
34885     
34886         this.maxY = 0;
34887     },
34888
34889     measureColumns : function()
34890     {
34891         this.getContainerWidth();
34892       // if columnWidth is 0, default to outerWidth of first item
34893         if ( !this.columnWidth ) {
34894             var firstItem = this.bricks.first();
34895             Roo.log(firstItem);
34896             this.columnWidth  = this.containerWidth;
34897             if (firstItem && firstItem.attr('originalwidth') ) {
34898                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34899             }
34900             // columnWidth fall back to item of first element
34901             Roo.log("set column width?");
34902                         this.initialColumnWidth = this.columnWidth  ;
34903
34904             // if first elem has no width, default to size of container
34905             
34906         }
34907         
34908         
34909         if (this.initialColumnWidth) {
34910             this.columnWidth = this.initialColumnWidth;
34911         }
34912         
34913         
34914             
34915         // column width is fixed at the top - however if container width get's smaller we should
34916         // reduce it...
34917         
34918         // this bit calcs how man columns..
34919             
34920         var columnWidth = this.columnWidth += this.gutter;
34921       
34922         // calculate columns
34923         var containerWidth = this.containerWidth + this.gutter;
34924         
34925         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34926         // fix rounding errors, typically with gutters
34927         var excess = columnWidth - containerWidth % columnWidth;
34928         
34929         
34930         // if overshoot is less than a pixel, round up, otherwise floor it
34931         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34932         cols = Math[ mathMethod ]( cols );
34933         this.cols = Math.max( cols, 1 );
34934         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34935         
34936          // padding positioning..
34937         var totalColWidth = this.cols * this.columnWidth;
34938         var padavail = this.containerWidth - totalColWidth;
34939         // so for 2 columns - we need 3 'pads'
34940         
34941         var padNeeded = (1+this.cols) * this.padWidth;
34942         
34943         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34944         
34945         this.columnWidth += padExtra
34946         //this.padWidth = Math.floor(padavail /  ( this.cols));
34947         
34948         // adjust colum width so that padding is fixed??
34949         
34950         // we have 3 columns ... total = width * 3
34951         // we have X left over... that should be used by 
34952         
34953         //if (this.expandC) {
34954             
34955         //}
34956         
34957         
34958         
34959     },
34960     
34961     getContainerWidth : function()
34962     {
34963        /* // container is parent if fit width
34964         var container = this.isFitWidth ? this.element.parentNode : this.element;
34965         // check that this.size and size are there
34966         // IE8 triggers resize on body size change, so they might not be
34967         
34968         var size = getSize( container );  //FIXME
34969         this.containerWidth = size && size.innerWidth; //FIXME
34970         */
34971          
34972         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34973         
34974     },
34975     
34976     _getItemLayoutPosition : function( item )  // what is item?
34977     {
34978         // we resize the item to our columnWidth..
34979       
34980         item.setWidth(this.columnWidth);
34981         item.autoBoxAdjust  = false;
34982         
34983         var sz = item.getSize();
34984  
34985         // how many columns does this brick span
34986         var remainder = this.containerWidth % this.columnWidth;
34987         
34988         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34989         // round if off by 1 pixel, otherwise use ceil
34990         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34991         colSpan = Math.min( colSpan, this.cols );
34992         
34993         // normally this should be '1' as we dont' currently allow multi width columns..
34994         
34995         var colGroup = this._getColGroup( colSpan );
34996         // get the minimum Y value from the columns
34997         var minimumY = Math.min.apply( Math, colGroup );
34998         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34999         
35000         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35001          
35002         // position the brick
35003         var position = {
35004             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35005             y: this.currentSize.y + minimumY + this.padHeight
35006         };
35007         
35008         Roo.log(position);
35009         // apply setHeight to necessary columns
35010         var setHeight = minimumY + sz.height + this.padHeight;
35011         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35012         
35013         var setSpan = this.cols + 1 - colGroup.length;
35014         for ( var i = 0; i < setSpan; i++ ) {
35015           this.colYs[ shortColIndex + i ] = setHeight ;
35016         }
35017       
35018         return position;
35019     },
35020     
35021     /**
35022      * @param {Number} colSpan - number of columns the element spans
35023      * @returns {Array} colGroup
35024      */
35025     _getColGroup : function( colSpan )
35026     {
35027         if ( colSpan < 2 ) {
35028           // if brick spans only one column, use all the column Ys
35029           return this.colYs;
35030         }
35031       
35032         var colGroup = [];
35033         // how many different places could this brick fit horizontally
35034         var groupCount = this.cols + 1 - colSpan;
35035         // for each group potential horizontal position
35036         for ( var i = 0; i < groupCount; i++ ) {
35037           // make an array of colY values for that one group
35038           var groupColYs = this.colYs.slice( i, i + colSpan );
35039           // and get the max value of the array
35040           colGroup[i] = Math.max.apply( Math, groupColYs );
35041         }
35042         return colGroup;
35043     },
35044     /*
35045     _manageStamp : function( stamp )
35046     {
35047         var stampSize =  stamp.getSize();
35048         var offset = stamp.getBox();
35049         // get the columns that this stamp affects
35050         var firstX = this.isOriginLeft ? offset.x : offset.right;
35051         var lastX = firstX + stampSize.width;
35052         var firstCol = Math.floor( firstX / this.columnWidth );
35053         firstCol = Math.max( 0, firstCol );
35054         
35055         var lastCol = Math.floor( lastX / this.columnWidth );
35056         // lastCol should not go over if multiple of columnWidth #425
35057         lastCol -= lastX % this.columnWidth ? 0 : 1;
35058         lastCol = Math.min( this.cols - 1, lastCol );
35059         
35060         // set colYs to bottom of the stamp
35061         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35062             stampSize.height;
35063             
35064         for ( var i = firstCol; i <= lastCol; i++ ) {
35065           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35066         }
35067     },
35068     */
35069     
35070     _getContainerSize : function()
35071     {
35072         this.maxY = Math.max.apply( Math, this.colYs );
35073         var size = {
35074             height: this.maxY
35075         };
35076       
35077         if ( this.isFitWidth ) {
35078             size.width = this._getContainerFitWidth();
35079         }
35080       
35081         return size;
35082     },
35083     
35084     _getContainerFitWidth : function()
35085     {
35086         var unusedCols = 0;
35087         // count unused columns
35088         var i = this.cols;
35089         while ( --i ) {
35090           if ( this.colYs[i] !== 0 ) {
35091             break;
35092           }
35093           unusedCols++;
35094         }
35095         // fit container to columns that have been used
35096         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35097     },
35098     
35099     needsResizeLayout : function()
35100     {
35101         var previousWidth = this.containerWidth;
35102         this.getContainerWidth();
35103         return previousWidth !== this.containerWidth;
35104     }
35105  
35106 });
35107
35108  
35109
35110  /*
35111  * - LGPL
35112  *
35113  * element
35114  * 
35115  */
35116
35117 /**
35118  * @class Roo.bootstrap.MasonryBrick
35119  * @extends Roo.bootstrap.Component
35120  * Bootstrap MasonryBrick class
35121  * 
35122  * @constructor
35123  * Create a new MasonryBrick
35124  * @param {Object} config The config object
35125  */
35126
35127 Roo.bootstrap.MasonryBrick = function(config){
35128     
35129     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35130     
35131     Roo.bootstrap.MasonryBrick.register(this);
35132     
35133     this.addEvents({
35134         // raw events
35135         /**
35136          * @event click
35137          * When a MasonryBrick is clcik
35138          * @param {Roo.bootstrap.MasonryBrick} this
35139          * @param {Roo.EventObject} e
35140          */
35141         "click" : true
35142     });
35143 };
35144
35145 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35146     
35147     /**
35148      * @cfg {String} title
35149      */   
35150     title : '',
35151     /**
35152      * @cfg {String} html
35153      */   
35154     html : '',
35155     /**
35156      * @cfg {String} bgimage
35157      */   
35158     bgimage : '',
35159     /**
35160      * @cfg {String} videourl
35161      */   
35162     videourl : '',
35163     /**
35164      * @cfg {String} cls
35165      */   
35166     cls : '',
35167     /**
35168      * @cfg {String} href
35169      */   
35170     href : '',
35171     /**
35172      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35173      */   
35174     size : 'xs',
35175     
35176     /**
35177      * @cfg {String} placetitle (center|bottom)
35178      */   
35179     placetitle : '',
35180     
35181     /**
35182      * @cfg {Boolean} isFitContainer defalut true
35183      */   
35184     isFitContainer : true, 
35185     
35186     /**
35187      * @cfg {Boolean} preventDefault defalut false
35188      */   
35189     preventDefault : false, 
35190     
35191     /**
35192      * @cfg {Boolean} inverse defalut false
35193      */   
35194     maskInverse : false, 
35195     
35196     getAutoCreate : function()
35197     {
35198         if(!this.isFitContainer){
35199             return this.getSplitAutoCreate();
35200         }
35201         
35202         var cls = 'masonry-brick masonry-brick-full';
35203         
35204         if(this.href.length){
35205             cls += ' masonry-brick-link';
35206         }
35207         
35208         if(this.bgimage.length){
35209             cls += ' masonry-brick-image';
35210         }
35211         
35212         if(this.maskInverse){
35213             cls += ' mask-inverse';
35214         }
35215         
35216         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35217             cls += ' enable-mask';
35218         }
35219         
35220         if(this.size){
35221             cls += ' masonry-' + this.size + '-brick';
35222         }
35223         
35224         if(this.placetitle.length){
35225             
35226             switch (this.placetitle) {
35227                 case 'center' :
35228                     cls += ' masonry-center-title';
35229                     break;
35230                 case 'bottom' :
35231                     cls += ' masonry-bottom-title';
35232                     break;
35233                 default:
35234                     break;
35235             }
35236             
35237         } else {
35238             if(!this.html.length && !this.bgimage.length){
35239                 cls += ' masonry-center-title';
35240             }
35241
35242             if(!this.html.length && this.bgimage.length){
35243                 cls += ' masonry-bottom-title';
35244             }
35245         }
35246         
35247         if(this.cls){
35248             cls += ' ' + this.cls;
35249         }
35250         
35251         var cfg = {
35252             tag: (this.href.length) ? 'a' : 'div',
35253             cls: cls,
35254             cn: [
35255                 {
35256                     tag: 'div',
35257                     cls: 'masonry-brick-mask'
35258                 },
35259                 {
35260                     tag: 'div',
35261                     cls: 'masonry-brick-paragraph',
35262                     cn: []
35263                 }
35264             ]
35265         };
35266         
35267         if(this.href.length){
35268             cfg.href = this.href;
35269         }
35270         
35271         var cn = cfg.cn[1].cn;
35272         
35273         if(this.title.length){
35274             cn.push({
35275                 tag: 'h4',
35276                 cls: 'masonry-brick-title',
35277                 html: this.title
35278             });
35279         }
35280         
35281         if(this.html.length){
35282             cn.push({
35283                 tag: 'p',
35284                 cls: 'masonry-brick-text',
35285                 html: this.html
35286             });
35287         }
35288         
35289         if (!this.title.length && !this.html.length) {
35290             cfg.cn[1].cls += ' hide';
35291         }
35292         
35293         if(this.bgimage.length){
35294             cfg.cn.push({
35295                 tag: 'img',
35296                 cls: 'masonry-brick-image-view',
35297                 src: this.bgimage
35298             });
35299         }
35300         
35301         if(this.videourl.length){
35302             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35303             // youtube support only?
35304             cfg.cn.push({
35305                 tag: 'iframe',
35306                 cls: 'masonry-brick-image-view',
35307                 src: vurl,
35308                 frameborder : 0,
35309                 allowfullscreen : true
35310             });
35311         }
35312         
35313         return cfg;
35314         
35315     },
35316     
35317     getSplitAutoCreate : function()
35318     {
35319         var cls = 'masonry-brick masonry-brick-split';
35320         
35321         if(this.href.length){
35322             cls += ' masonry-brick-link';
35323         }
35324         
35325         if(this.bgimage.length){
35326             cls += ' masonry-brick-image';
35327         }
35328         
35329         if(this.size){
35330             cls += ' masonry-' + this.size + '-brick';
35331         }
35332         
35333         switch (this.placetitle) {
35334             case 'center' :
35335                 cls += ' masonry-center-title';
35336                 break;
35337             case 'bottom' :
35338                 cls += ' masonry-bottom-title';
35339                 break;
35340             default:
35341                 if(!this.bgimage.length){
35342                     cls += ' masonry-center-title';
35343                 }
35344
35345                 if(this.bgimage.length){
35346                     cls += ' masonry-bottom-title';
35347                 }
35348                 break;
35349         }
35350         
35351         if(this.cls){
35352             cls += ' ' + this.cls;
35353         }
35354         
35355         var cfg = {
35356             tag: (this.href.length) ? 'a' : 'div',
35357             cls: cls,
35358             cn: [
35359                 {
35360                     tag: 'div',
35361                     cls: 'masonry-brick-split-head',
35362                     cn: [
35363                         {
35364                             tag: 'div',
35365                             cls: 'masonry-brick-paragraph',
35366                             cn: []
35367                         }
35368                     ]
35369                 },
35370                 {
35371                     tag: 'div',
35372                     cls: 'masonry-brick-split-body',
35373                     cn: []
35374                 }
35375             ]
35376         };
35377         
35378         if(this.href.length){
35379             cfg.href = this.href;
35380         }
35381         
35382         if(this.title.length){
35383             cfg.cn[0].cn[0].cn.push({
35384                 tag: 'h4',
35385                 cls: 'masonry-brick-title',
35386                 html: this.title
35387             });
35388         }
35389         
35390         if(this.html.length){
35391             cfg.cn[1].cn.push({
35392                 tag: 'p',
35393                 cls: 'masonry-brick-text',
35394                 html: this.html
35395             });
35396         }
35397
35398         if(this.bgimage.length){
35399             cfg.cn[0].cn.push({
35400                 tag: 'img',
35401                 cls: 'masonry-brick-image-view',
35402                 src: this.bgimage
35403             });
35404         }
35405         
35406         if(this.videourl.length){
35407             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35408             // youtube support only?
35409             cfg.cn[0].cn.cn.push({
35410                 tag: 'iframe',
35411                 cls: 'masonry-brick-image-view',
35412                 src: vurl,
35413                 frameborder : 0,
35414                 allowfullscreen : true
35415             });
35416         }
35417         
35418         return cfg;
35419     },
35420     
35421     initEvents: function() 
35422     {
35423         switch (this.size) {
35424             case 'xs' :
35425                 this.x = 1;
35426                 this.y = 1;
35427                 break;
35428             case 'sm' :
35429                 this.x = 2;
35430                 this.y = 2;
35431                 break;
35432             case 'md' :
35433             case 'md-left' :
35434             case 'md-right' :
35435                 this.x = 3;
35436                 this.y = 3;
35437                 break;
35438             case 'tall' :
35439                 this.x = 2;
35440                 this.y = 3;
35441                 break;
35442             case 'wide' :
35443                 this.x = 3;
35444                 this.y = 2;
35445                 break;
35446             case 'wide-thin' :
35447                 this.x = 3;
35448                 this.y = 1;
35449                 break;
35450                         
35451             default :
35452                 break;
35453         }
35454         
35455         if(Roo.isTouch){
35456             this.el.on('touchstart', this.onTouchStart, this);
35457             this.el.on('touchmove', this.onTouchMove, this);
35458             this.el.on('touchend', this.onTouchEnd, this);
35459             this.el.on('contextmenu', this.onContextMenu, this);
35460         } else {
35461             this.el.on('mouseenter'  ,this.enter, this);
35462             this.el.on('mouseleave', this.leave, this);
35463             this.el.on('click', this.onClick, this);
35464         }
35465         
35466         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35467             this.parent().bricks.push(this);   
35468         }
35469         
35470     },
35471     
35472     onClick: function(e, el)
35473     {
35474         var time = this.endTimer - this.startTimer;
35475         // Roo.log(e.preventDefault());
35476         if(Roo.isTouch){
35477             if(time > 1000){
35478                 e.preventDefault();
35479                 return;
35480             }
35481         }
35482         
35483         if(!this.preventDefault){
35484             return;
35485         }
35486         
35487         e.preventDefault();
35488         
35489         if (this.activeClass != '') {
35490             this.selectBrick();
35491         }
35492         
35493         this.fireEvent('click', this, e);
35494     },
35495     
35496     enter: function(e, el)
35497     {
35498         e.preventDefault();
35499         
35500         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35501             return;
35502         }
35503         
35504         if(this.bgimage.length && this.html.length){
35505             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35506         }
35507     },
35508     
35509     leave: 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, true);
35519         }
35520     },
35521     
35522     onTouchStart: function(e, el)
35523     {
35524 //        e.preventDefault();
35525         
35526         this.touchmoved = false;
35527         
35528         if(!this.isFitContainer){
35529             return;
35530         }
35531         
35532         if(!this.bgimage.length || !this.html.length){
35533             return;
35534         }
35535         
35536         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35537         
35538         this.timer = new Date().getTime();
35539         
35540     },
35541     
35542     onTouchMove: function(e, el)
35543     {
35544         this.touchmoved = true;
35545     },
35546     
35547     onContextMenu : function(e,el)
35548     {
35549         e.preventDefault();
35550         e.stopPropagation();
35551         return false;
35552     },
35553     
35554     onTouchEnd: function(e, el)
35555     {
35556 //        e.preventDefault();
35557         
35558         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35559         
35560             this.leave(e,el);
35561             
35562             return;
35563         }
35564         
35565         if(!this.bgimage.length || !this.html.length){
35566             
35567             if(this.href.length){
35568                 window.location.href = this.href;
35569             }
35570             
35571             return;
35572         }
35573         
35574         if(!this.isFitContainer){
35575             return;
35576         }
35577         
35578         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35579         
35580         window.location.href = this.href;
35581     },
35582     
35583     //selection on single brick only
35584     selectBrick : function() {
35585         
35586         if (!this.parentId) {
35587             return;
35588         }
35589         
35590         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35591         var index = m.selectedBrick.indexOf(this.id);
35592         
35593         if ( index > -1) {
35594             m.selectedBrick.splice(index,1);
35595             this.el.removeClass(this.activeClass);
35596             return;
35597         }
35598         
35599         for(var i = 0; i < m.selectedBrick.length; i++) {
35600             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35601             b.el.removeClass(b.activeClass);
35602         }
35603         
35604         m.selectedBrick = [];
35605         
35606         m.selectedBrick.push(this.id);
35607         this.el.addClass(this.activeClass);
35608         return;
35609     },
35610     
35611     isSelected : function(){
35612         return this.el.hasClass(this.activeClass);
35613         
35614     }
35615 });
35616
35617 Roo.apply(Roo.bootstrap.MasonryBrick, {
35618     
35619     //groups: {},
35620     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35621      /**
35622     * register a Masonry Brick
35623     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35624     */
35625     
35626     register : function(brick)
35627     {
35628         //this.groups[brick.id] = brick;
35629         this.groups.add(brick.id, brick);
35630     },
35631     /**
35632     * fetch a  masonry brick based on the masonry brick ID
35633     * @param {string} the masonry brick to add
35634     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35635     */
35636     
35637     get: function(brick_id) 
35638     {
35639         // if (typeof(this.groups[brick_id]) == 'undefined') {
35640         //     return false;
35641         // }
35642         // return this.groups[brick_id] ;
35643         
35644         if(this.groups.key(brick_id)) {
35645             return this.groups.key(brick_id);
35646         }
35647         
35648         return false;
35649     }
35650     
35651     
35652     
35653 });
35654
35655  /*
35656  * - LGPL
35657  *
35658  * element
35659  * 
35660  */
35661
35662 /**
35663  * @class Roo.bootstrap.Brick
35664  * @extends Roo.bootstrap.Component
35665  * Bootstrap Brick class
35666  * 
35667  * @constructor
35668  * Create a new Brick
35669  * @param {Object} config The config object
35670  */
35671
35672 Roo.bootstrap.Brick = function(config){
35673     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35674     
35675     this.addEvents({
35676         // raw events
35677         /**
35678          * @event click
35679          * When a Brick is click
35680          * @param {Roo.bootstrap.Brick} this
35681          * @param {Roo.EventObject} e
35682          */
35683         "click" : true
35684     });
35685 };
35686
35687 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35688     
35689     /**
35690      * @cfg {String} title
35691      */   
35692     title : '',
35693     /**
35694      * @cfg {String} html
35695      */   
35696     html : '',
35697     /**
35698      * @cfg {String} bgimage
35699      */   
35700     bgimage : '',
35701     /**
35702      * @cfg {String} cls
35703      */   
35704     cls : '',
35705     /**
35706      * @cfg {String} href
35707      */   
35708     href : '',
35709     /**
35710      * @cfg {String} video
35711      */   
35712     video : '',
35713     /**
35714      * @cfg {Boolean} square
35715      */   
35716     square : true,
35717     
35718     getAutoCreate : function()
35719     {
35720         var cls = 'roo-brick';
35721         
35722         if(this.href.length){
35723             cls += ' roo-brick-link';
35724         }
35725         
35726         if(this.bgimage.length){
35727             cls += ' roo-brick-image';
35728         }
35729         
35730         if(!this.html.length && !this.bgimage.length){
35731             cls += ' roo-brick-center-title';
35732         }
35733         
35734         if(!this.html.length && this.bgimage.length){
35735             cls += ' roo-brick-bottom-title';
35736         }
35737         
35738         if(this.cls){
35739             cls += ' ' + this.cls;
35740         }
35741         
35742         var cfg = {
35743             tag: (this.href.length) ? 'a' : 'div',
35744             cls: cls,
35745             cn: [
35746                 {
35747                     tag: 'div',
35748                     cls: 'roo-brick-paragraph',
35749                     cn: []
35750                 }
35751             ]
35752         };
35753         
35754         if(this.href.length){
35755             cfg.href = this.href;
35756         }
35757         
35758         var cn = cfg.cn[0].cn;
35759         
35760         if(this.title.length){
35761             cn.push({
35762                 tag: 'h4',
35763                 cls: 'roo-brick-title',
35764                 html: this.title
35765             });
35766         }
35767         
35768         if(this.html.length){
35769             cn.push({
35770                 tag: 'p',
35771                 cls: 'roo-brick-text',
35772                 html: this.html
35773             });
35774         } else {
35775             cn.cls += ' hide';
35776         }
35777         
35778         if(this.bgimage.length){
35779             cfg.cn.push({
35780                 tag: 'img',
35781                 cls: 'roo-brick-image-view',
35782                 src: this.bgimage
35783             });
35784         }
35785         
35786         return cfg;
35787     },
35788     
35789     initEvents: function() 
35790     {
35791         if(this.title.length || this.html.length){
35792             this.el.on('mouseenter'  ,this.enter, this);
35793             this.el.on('mouseleave', this.leave, this);
35794         }
35795         
35796         Roo.EventManager.onWindowResize(this.resize, this); 
35797         
35798         if(this.bgimage.length){
35799             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35800             this.imageEl.on('load', this.onImageLoad, this);
35801             return;
35802         }
35803         
35804         this.resize();
35805     },
35806     
35807     onImageLoad : function()
35808     {
35809         this.resize();
35810     },
35811     
35812     resize : function()
35813     {
35814         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35815         
35816         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35817         
35818         if(this.bgimage.length){
35819             var image = this.el.select('.roo-brick-image-view', true).first();
35820             
35821             image.setWidth(paragraph.getWidth());
35822             
35823             if(this.square){
35824                 image.setHeight(paragraph.getWidth());
35825             }
35826             
35827             this.el.setHeight(image.getHeight());
35828             paragraph.setHeight(image.getHeight());
35829             
35830         }
35831         
35832     },
35833     
35834     enter: function(e, el)
35835     {
35836         e.preventDefault();
35837         
35838         if(this.bgimage.length){
35839             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35840             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35841         }
35842     },
35843     
35844     leave: function(e, el)
35845     {
35846         e.preventDefault();
35847         
35848         if(this.bgimage.length){
35849             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35850             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35851         }
35852     }
35853     
35854 });
35855
35856  
35857
35858  /*
35859  * - LGPL
35860  *
35861  * Number field 
35862  */
35863
35864 /**
35865  * @class Roo.bootstrap.NumberField
35866  * @extends Roo.bootstrap.Input
35867  * Bootstrap NumberField class
35868  * 
35869  * 
35870  * 
35871  * 
35872  * @constructor
35873  * Create a new NumberField
35874  * @param {Object} config The config object
35875  */
35876
35877 Roo.bootstrap.NumberField = function(config){
35878     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35879 };
35880
35881 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35882     
35883     /**
35884      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35885      */
35886     allowDecimals : true,
35887     /**
35888      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35889      */
35890     decimalSeparator : ".",
35891     /**
35892      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35893      */
35894     decimalPrecision : 2,
35895     /**
35896      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35897      */
35898     allowNegative : true,
35899     
35900     /**
35901      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35902      */
35903     allowZero: true,
35904     /**
35905      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35906      */
35907     minValue : Number.NEGATIVE_INFINITY,
35908     /**
35909      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35910      */
35911     maxValue : Number.MAX_VALUE,
35912     /**
35913      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35914      */
35915     minText : "The minimum value for this field is {0}",
35916     /**
35917      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35918      */
35919     maxText : "The maximum value for this field is {0}",
35920     /**
35921      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35922      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35923      */
35924     nanText : "{0} is not a valid number",
35925     /**
35926      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35927      */
35928     thousandsDelimiter : false,
35929     /**
35930      * @cfg {String} valueAlign alignment of value
35931      */
35932     valueAlign : "left",
35933
35934     getAutoCreate : function()
35935     {
35936         var hiddenInput = {
35937             tag: 'input',
35938             type: 'hidden',
35939             id: Roo.id(),
35940             cls: 'hidden-number-input'
35941         };
35942         
35943         if (this.name) {
35944             hiddenInput.name = this.name;
35945         }
35946         
35947         this.name = '';
35948         
35949         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35950         
35951         this.name = hiddenInput.name;
35952         
35953         if(cfg.cn.length > 0) {
35954             cfg.cn.push(hiddenInput);
35955         }
35956         
35957         return cfg;
35958     },
35959
35960     // private
35961     initEvents : function()
35962     {   
35963         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35964         
35965         var allowed = "0123456789";
35966         
35967         if(this.allowDecimals){
35968             allowed += this.decimalSeparator;
35969         }
35970         
35971         if(this.allowNegative){
35972             allowed += "-";
35973         }
35974         
35975         if(this.thousandsDelimiter) {
35976             allowed += ",";
35977         }
35978         
35979         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35980         
35981         var keyPress = function(e){
35982             
35983             var k = e.getKey();
35984             
35985             var c = e.getCharCode();
35986             
35987             if(
35988                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35989                     allowed.indexOf(String.fromCharCode(c)) === -1
35990             ){
35991                 e.stopEvent();
35992                 return;
35993             }
35994             
35995             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35996                 return;
35997             }
35998             
35999             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36000                 e.stopEvent();
36001             }
36002         };
36003         
36004         this.el.on("keypress", keyPress, this);
36005     },
36006     
36007     validateValue : function(value)
36008     {
36009         
36010         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36011             return false;
36012         }
36013         
36014         var num = this.parseValue(value);
36015         
36016         if(isNaN(num)){
36017             this.markInvalid(String.format(this.nanText, value));
36018             return false;
36019         }
36020         
36021         if(num < this.minValue){
36022             this.markInvalid(String.format(this.minText, this.minValue));
36023             return false;
36024         }
36025         
36026         if(num > this.maxValue){
36027             this.markInvalid(String.format(this.maxText, this.maxValue));
36028             return false;
36029         }
36030         
36031         return true;
36032     },
36033
36034     getValue : function()
36035     {
36036         var v = this.hiddenEl().getValue();
36037         
36038         return this.fixPrecision(this.parseValue(v));
36039     },
36040
36041     parseValue : function(value)
36042     {
36043         if(this.thousandsDelimiter) {
36044             value += "";
36045             r = new RegExp(",", "g");
36046             value = value.replace(r, "");
36047         }
36048         
36049         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36050         return isNaN(value) ? '' : value;
36051     },
36052
36053     fixPrecision : function(value)
36054     {
36055         if(this.thousandsDelimiter) {
36056             value += "";
36057             r = new RegExp(",", "g");
36058             value = value.replace(r, "");
36059         }
36060         
36061         var nan = isNaN(value);
36062         
36063         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36064             return nan ? '' : value;
36065         }
36066         return parseFloat(value).toFixed(this.decimalPrecision);
36067     },
36068
36069     setValue : function(v)
36070     {
36071         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36072         
36073         this.value = v;
36074         
36075         if(this.rendered){
36076             
36077             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36078             
36079             this.inputEl().dom.value = (v == '') ? '' :
36080                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36081             
36082             if(!this.allowZero && v === '0') {
36083                 this.hiddenEl().dom.value = '';
36084                 this.inputEl().dom.value = '';
36085             }
36086             
36087             this.validate();
36088         }
36089     },
36090
36091     decimalPrecisionFcn : function(v)
36092     {
36093         return Math.floor(v);
36094     },
36095
36096     beforeBlur : function()
36097     {
36098         var v = this.parseValue(this.getRawValue());
36099         
36100         if(v || v === 0 || v === ''){
36101             this.setValue(v);
36102         }
36103     },
36104     
36105     hiddenEl : function()
36106     {
36107         return this.el.select('input.hidden-number-input',true).first();
36108     }
36109     
36110 });
36111
36112  
36113
36114 /*
36115 * Licence: LGPL
36116 */
36117
36118 /**
36119  * @class Roo.bootstrap.DocumentSlider
36120  * @extends Roo.bootstrap.Component
36121  * Bootstrap DocumentSlider class
36122  * 
36123  * @constructor
36124  * Create a new DocumentViewer
36125  * @param {Object} config The config object
36126  */
36127
36128 Roo.bootstrap.DocumentSlider = function(config){
36129     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36130     
36131     this.files = [];
36132     
36133     this.addEvents({
36134         /**
36135          * @event initial
36136          * Fire after initEvent
36137          * @param {Roo.bootstrap.DocumentSlider} this
36138          */
36139         "initial" : true,
36140         /**
36141          * @event update
36142          * Fire after update
36143          * @param {Roo.bootstrap.DocumentSlider} this
36144          */
36145         "update" : true,
36146         /**
36147          * @event click
36148          * Fire after click
36149          * @param {Roo.bootstrap.DocumentSlider} this
36150          */
36151         "click" : true
36152     });
36153 };
36154
36155 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36156     
36157     files : false,
36158     
36159     indicator : 0,
36160     
36161     getAutoCreate : function()
36162     {
36163         var cfg = {
36164             tag : 'div',
36165             cls : 'roo-document-slider',
36166             cn : [
36167                 {
36168                     tag : 'div',
36169                     cls : 'roo-document-slider-header',
36170                     cn : [
36171                         {
36172                             tag : 'div',
36173                             cls : 'roo-document-slider-header-title'
36174                         }
36175                     ]
36176                 },
36177                 {
36178                     tag : 'div',
36179                     cls : 'roo-document-slider-body',
36180                     cn : [
36181                         {
36182                             tag : 'div',
36183                             cls : 'roo-document-slider-prev',
36184                             cn : [
36185                                 {
36186                                     tag : 'i',
36187                                     cls : 'fa fa-chevron-left'
36188                                 }
36189                             ]
36190                         },
36191                         {
36192                             tag : 'div',
36193                             cls : 'roo-document-slider-thumb',
36194                             cn : [
36195                                 {
36196                                     tag : 'img',
36197                                     cls : 'roo-document-slider-image'
36198                                 }
36199                             ]
36200                         },
36201                         {
36202                             tag : 'div',
36203                             cls : 'roo-document-slider-next',
36204                             cn : [
36205                                 {
36206                                     tag : 'i',
36207                                     cls : 'fa fa-chevron-right'
36208                                 }
36209                             ]
36210                         }
36211                     ]
36212                 }
36213             ]
36214         };
36215         
36216         return cfg;
36217     },
36218     
36219     initEvents : function()
36220     {
36221         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36222         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36223         
36224         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36225         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36226         
36227         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36228         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36229         
36230         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36231         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36232         
36233         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36234         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36235         
36236         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36237         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36238         
36239         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36240         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36241         
36242         this.thumbEl.on('click', this.onClick, this);
36243         
36244         this.prevIndicator.on('click', this.prev, this);
36245         
36246         this.nextIndicator.on('click', this.next, this);
36247         
36248     },
36249     
36250     initial : function()
36251     {
36252         if(this.files.length){
36253             this.indicator = 1;
36254             this.update()
36255         }
36256         
36257         this.fireEvent('initial', this);
36258     },
36259     
36260     update : function()
36261     {
36262         this.imageEl.attr('src', this.files[this.indicator - 1]);
36263         
36264         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36265         
36266         this.prevIndicator.show();
36267         
36268         if(this.indicator == 1){
36269             this.prevIndicator.hide();
36270         }
36271         
36272         this.nextIndicator.show();
36273         
36274         if(this.indicator == this.files.length){
36275             this.nextIndicator.hide();
36276         }
36277         
36278         this.thumbEl.scrollTo('top');
36279         
36280         this.fireEvent('update', this);
36281     },
36282     
36283     onClick : function(e)
36284     {
36285         e.preventDefault();
36286         
36287         this.fireEvent('click', this);
36288     },
36289     
36290     prev : function(e)
36291     {
36292         e.preventDefault();
36293         
36294         this.indicator = Math.max(1, this.indicator - 1);
36295         
36296         this.update();
36297     },
36298     
36299     next : function(e)
36300     {
36301         e.preventDefault();
36302         
36303         this.indicator = Math.min(this.files.length, this.indicator + 1);
36304         
36305         this.update();
36306     }
36307 });
36308 /*
36309  * - LGPL
36310  *
36311  * RadioSet
36312  *
36313  *
36314  */
36315
36316 /**
36317  * @class Roo.bootstrap.RadioSet
36318  * @extends Roo.bootstrap.Input
36319  * Bootstrap RadioSet class
36320  * @cfg {String} indicatorpos (left|right) default left
36321  * @cfg {Boolean} inline (true|false) inline the element (default true)
36322  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36323  * @constructor
36324  * Create a new RadioSet
36325  * @param {Object} config The config object
36326  */
36327
36328 Roo.bootstrap.RadioSet = function(config){
36329     
36330     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36331     
36332     this.radioes = [];
36333     
36334     Roo.bootstrap.RadioSet.register(this);
36335     
36336     this.addEvents({
36337         /**
36338         * @event check
36339         * Fires when the element is checked or unchecked.
36340         * @param {Roo.bootstrap.RadioSet} this This radio
36341         * @param {Roo.bootstrap.Radio} item The checked item
36342         */
36343        check : true,
36344        /**
36345         * @event click
36346         * Fires when the element is click.
36347         * @param {Roo.bootstrap.RadioSet} this This radio set
36348         * @param {Roo.bootstrap.Radio} item The checked item
36349         * @param {Roo.EventObject} e The event object
36350         */
36351        click : true
36352     });
36353     
36354 };
36355
36356 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36357
36358     radioes : false,
36359     
36360     inline : true,
36361     
36362     weight : '',
36363     
36364     indicatorpos : 'left',
36365     
36366     getAutoCreate : function()
36367     {
36368         var label = {
36369             tag : 'label',
36370             cls : 'roo-radio-set-label',
36371             cn : [
36372                 {
36373                     tag : 'span',
36374                     html : this.fieldLabel
36375                 }
36376             ]
36377         };
36378         if (Roo.bootstrap.version == 3) {
36379             
36380             
36381             if(this.indicatorpos == 'left'){
36382                 label.cn.unshift({
36383                     tag : 'i',
36384                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36385                     tooltip : 'This field is required'
36386                 });
36387             } else {
36388                 label.cn.push({
36389                     tag : 'i',
36390                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36391                     tooltip : 'This field is required'
36392                 });
36393             }
36394         }
36395         var items = {
36396             tag : 'div',
36397             cls : 'roo-radio-set-items'
36398         };
36399         
36400         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36401         
36402         if (align === 'left' && this.fieldLabel.length) {
36403             
36404             items = {
36405                 cls : "roo-radio-set-right", 
36406                 cn: [
36407                     items
36408                 ]
36409             };
36410             
36411             if(this.labelWidth > 12){
36412                 label.style = "width: " + this.labelWidth + 'px';
36413             }
36414             
36415             if(this.labelWidth < 13 && this.labelmd == 0){
36416                 this.labelmd = this.labelWidth;
36417             }
36418             
36419             if(this.labellg > 0){
36420                 label.cls += ' col-lg-' + this.labellg;
36421                 items.cls += ' col-lg-' + (12 - this.labellg);
36422             }
36423             
36424             if(this.labelmd > 0){
36425                 label.cls += ' col-md-' + this.labelmd;
36426                 items.cls += ' col-md-' + (12 - this.labelmd);
36427             }
36428             
36429             if(this.labelsm > 0){
36430                 label.cls += ' col-sm-' + this.labelsm;
36431                 items.cls += ' col-sm-' + (12 - this.labelsm);
36432             }
36433             
36434             if(this.labelxs > 0){
36435                 label.cls += ' col-xs-' + this.labelxs;
36436                 items.cls += ' col-xs-' + (12 - this.labelxs);
36437             }
36438         }
36439         
36440         var cfg = {
36441             tag : 'div',
36442             cls : 'roo-radio-set',
36443             cn : [
36444                 {
36445                     tag : 'input',
36446                     cls : 'roo-radio-set-input',
36447                     type : 'hidden',
36448                     name : this.name,
36449                     value : this.value ? this.value :  ''
36450                 },
36451                 label,
36452                 items
36453             ]
36454         };
36455         
36456         if(this.weight.length){
36457             cfg.cls += ' roo-radio-' + this.weight;
36458         }
36459         
36460         if(this.inline) {
36461             cfg.cls += ' roo-radio-set-inline';
36462         }
36463         
36464         var settings=this;
36465         ['xs','sm','md','lg'].map(function(size){
36466             if (settings[size]) {
36467                 cfg.cls += ' col-' + size + '-' + settings[size];
36468             }
36469         });
36470         
36471         return cfg;
36472         
36473     },
36474
36475     initEvents : function()
36476     {
36477         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36478         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36479         
36480         if(!this.fieldLabel.length){
36481             this.labelEl.hide();
36482         }
36483         
36484         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36485         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36486         
36487         this.indicator = this.indicatorEl();
36488         
36489         if(this.indicator){
36490             this.indicator.addClass('invisible');
36491         }
36492         
36493         this.originalValue = this.getValue();
36494         
36495     },
36496     
36497     inputEl: function ()
36498     {
36499         return this.el.select('.roo-radio-set-input', true).first();
36500     },
36501     
36502     getChildContainer : function()
36503     {
36504         return this.itemsEl;
36505     },
36506     
36507     register : function(item)
36508     {
36509         this.radioes.push(item);
36510         
36511     },
36512     
36513     validate : function()
36514     {   
36515         if(this.getVisibilityEl().hasClass('hidden')){
36516             return true;
36517         }
36518         
36519         var valid = false;
36520         
36521         Roo.each(this.radioes, function(i){
36522             if(!i.checked){
36523                 return;
36524             }
36525             
36526             valid = true;
36527             return false;
36528         });
36529         
36530         if(this.allowBlank) {
36531             return true;
36532         }
36533         
36534         if(this.disabled || valid){
36535             this.markValid();
36536             return true;
36537         }
36538         
36539         this.markInvalid();
36540         return false;
36541         
36542     },
36543     
36544     markValid : function()
36545     {
36546         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36547             this.indicatorEl().removeClass('visible');
36548             this.indicatorEl().addClass('invisible');
36549         }
36550         
36551         
36552         if (Roo.bootstrap.version == 3) {
36553             this.el.removeClass([this.invalidClass, this.validClass]);
36554             this.el.addClass(this.validClass);
36555         } else {
36556             this.el.removeClass(['is-invalid','is-valid']);
36557             this.el.addClass(['is-valid']);
36558         }
36559         this.fireEvent('valid', this);
36560     },
36561     
36562     markInvalid : function(msg)
36563     {
36564         if(this.allowBlank || this.disabled){
36565             return;
36566         }
36567         
36568         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36569             this.indicatorEl().removeClass('invisible');
36570             this.indicatorEl().addClass('visible');
36571         }
36572         if (Roo.bootstrap.version == 3) {
36573             this.el.removeClass([this.invalidClass, this.validClass]);
36574             this.el.addClass(this.invalidClass);
36575         } else {
36576             this.el.removeClass(['is-invalid','is-valid']);
36577             this.el.addClass(['is-invalid']);
36578         }
36579         
36580         this.fireEvent('invalid', this, msg);
36581         
36582     },
36583     
36584     setValue : function(v, suppressEvent)
36585     {   
36586         if(this.value === v){
36587             return;
36588         }
36589         
36590         this.value = v;
36591         
36592         if(this.rendered){
36593             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36594         }
36595         
36596         Roo.each(this.radioes, function(i){
36597             i.checked = false;
36598             i.el.removeClass('checked');
36599         });
36600         
36601         Roo.each(this.radioes, function(i){
36602             
36603             if(i.value === v || i.value.toString() === v.toString()){
36604                 i.checked = true;
36605                 i.el.addClass('checked');
36606                 
36607                 if(suppressEvent !== true){
36608                     this.fireEvent('check', this, i);
36609                 }
36610                 
36611                 return false;
36612             }
36613             
36614         }, this);
36615         
36616         this.validate();
36617     },
36618     
36619     clearInvalid : function(){
36620         
36621         if(!this.el || this.preventMark){
36622             return;
36623         }
36624         
36625         this.el.removeClass([this.invalidClass]);
36626         
36627         this.fireEvent('valid', this);
36628     }
36629     
36630 });
36631
36632 Roo.apply(Roo.bootstrap.RadioSet, {
36633     
36634     groups: {},
36635     
36636     register : function(set)
36637     {
36638         this.groups[set.name] = set;
36639     },
36640     
36641     get: function(name) 
36642     {
36643         if (typeof(this.groups[name]) == 'undefined') {
36644             return false;
36645         }
36646         
36647         return this.groups[name] ;
36648     }
36649     
36650 });
36651 /*
36652  * Based on:
36653  * Ext JS Library 1.1.1
36654  * Copyright(c) 2006-2007, Ext JS, LLC.
36655  *
36656  * Originally Released Under LGPL - original licence link has changed is not relivant.
36657  *
36658  * Fork - LGPL
36659  * <script type="text/javascript">
36660  */
36661
36662
36663 /**
36664  * @class Roo.bootstrap.SplitBar
36665  * @extends Roo.util.Observable
36666  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36667  * <br><br>
36668  * Usage:
36669  * <pre><code>
36670 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36671                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36672 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36673 split.minSize = 100;
36674 split.maxSize = 600;
36675 split.animate = true;
36676 split.on('moved', splitterMoved);
36677 </code></pre>
36678  * @constructor
36679  * Create a new SplitBar
36680  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36681  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36682  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36683  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36684                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36685                         position of the SplitBar).
36686  */
36687 Roo.bootstrap.SplitBar = function(cfg){
36688     
36689     /** @private */
36690     
36691     //{
36692     //  dragElement : elm
36693     //  resizingElement: el,
36694         // optional..
36695     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36696     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36697         // existingProxy ???
36698     //}
36699     
36700     this.el = Roo.get(cfg.dragElement, true);
36701     this.el.dom.unselectable = "on";
36702     /** @private */
36703     this.resizingEl = Roo.get(cfg.resizingElement, true);
36704
36705     /**
36706      * @private
36707      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36708      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36709      * @type Number
36710      */
36711     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36712     
36713     /**
36714      * The minimum size of the resizing element. (Defaults to 0)
36715      * @type Number
36716      */
36717     this.minSize = 0;
36718     
36719     /**
36720      * The maximum size of the resizing element. (Defaults to 2000)
36721      * @type Number
36722      */
36723     this.maxSize = 2000;
36724     
36725     /**
36726      * Whether to animate the transition to the new size
36727      * @type Boolean
36728      */
36729     this.animate = false;
36730     
36731     /**
36732      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36733      * @type Boolean
36734      */
36735     this.useShim = false;
36736     
36737     /** @private */
36738     this.shim = null;
36739     
36740     if(!cfg.existingProxy){
36741         /** @private */
36742         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36743     }else{
36744         this.proxy = Roo.get(cfg.existingProxy).dom;
36745     }
36746     /** @private */
36747     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36748     
36749     /** @private */
36750     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36751     
36752     /** @private */
36753     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36754     
36755     /** @private */
36756     this.dragSpecs = {};
36757     
36758     /**
36759      * @private The adapter to use to positon and resize elements
36760      */
36761     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36762     this.adapter.init(this);
36763     
36764     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36765         /** @private */
36766         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36767         this.el.addClass("roo-splitbar-h");
36768     }else{
36769         /** @private */
36770         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36771         this.el.addClass("roo-splitbar-v");
36772     }
36773     
36774     this.addEvents({
36775         /**
36776          * @event resize
36777          * Fires when the splitter is moved (alias for {@link #event-moved})
36778          * @param {Roo.bootstrap.SplitBar} this
36779          * @param {Number} newSize the new width or height
36780          */
36781         "resize" : true,
36782         /**
36783          * @event moved
36784          * Fires when the splitter is moved
36785          * @param {Roo.bootstrap.SplitBar} this
36786          * @param {Number} newSize the new width or height
36787          */
36788         "moved" : true,
36789         /**
36790          * @event beforeresize
36791          * Fires before the splitter is dragged
36792          * @param {Roo.bootstrap.SplitBar} this
36793          */
36794         "beforeresize" : true,
36795
36796         "beforeapply" : true
36797     });
36798
36799     Roo.util.Observable.call(this);
36800 };
36801
36802 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36803     onStartProxyDrag : function(x, y){
36804         this.fireEvent("beforeresize", this);
36805         if(!this.overlay){
36806             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36807             o.unselectable();
36808             o.enableDisplayMode("block");
36809             // all splitbars share the same overlay
36810             Roo.bootstrap.SplitBar.prototype.overlay = o;
36811         }
36812         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36813         this.overlay.show();
36814         Roo.get(this.proxy).setDisplayed("block");
36815         var size = this.adapter.getElementSize(this);
36816         this.activeMinSize = this.getMinimumSize();;
36817         this.activeMaxSize = this.getMaximumSize();;
36818         var c1 = size - this.activeMinSize;
36819         var c2 = Math.max(this.activeMaxSize - size, 0);
36820         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36821             this.dd.resetConstraints();
36822             this.dd.setXConstraint(
36823                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36824                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36825             );
36826             this.dd.setYConstraint(0, 0);
36827         }else{
36828             this.dd.resetConstraints();
36829             this.dd.setXConstraint(0, 0);
36830             this.dd.setYConstraint(
36831                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36832                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36833             );
36834          }
36835         this.dragSpecs.startSize = size;
36836         this.dragSpecs.startPoint = [x, y];
36837         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36838     },
36839     
36840     /** 
36841      * @private Called after the drag operation by the DDProxy
36842      */
36843     onEndProxyDrag : function(e){
36844         Roo.get(this.proxy).setDisplayed(false);
36845         var endPoint = Roo.lib.Event.getXY(e);
36846         if(this.overlay){
36847             this.overlay.hide();
36848         }
36849         var newSize;
36850         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36851             newSize = this.dragSpecs.startSize + 
36852                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36853                     endPoint[0] - this.dragSpecs.startPoint[0] :
36854                     this.dragSpecs.startPoint[0] - endPoint[0]
36855                 );
36856         }else{
36857             newSize = this.dragSpecs.startSize + 
36858                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36859                     endPoint[1] - this.dragSpecs.startPoint[1] :
36860                     this.dragSpecs.startPoint[1] - endPoint[1]
36861                 );
36862         }
36863         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36864         if(newSize != this.dragSpecs.startSize){
36865             if(this.fireEvent('beforeapply', this, newSize) !== false){
36866                 this.adapter.setElementSize(this, newSize);
36867                 this.fireEvent("moved", this, newSize);
36868                 this.fireEvent("resize", this, newSize);
36869             }
36870         }
36871     },
36872     
36873     /**
36874      * Get the adapter this SplitBar uses
36875      * @return The adapter object
36876      */
36877     getAdapter : function(){
36878         return this.adapter;
36879     },
36880     
36881     /**
36882      * Set the adapter this SplitBar uses
36883      * @param {Object} adapter A SplitBar adapter object
36884      */
36885     setAdapter : function(adapter){
36886         this.adapter = adapter;
36887         this.adapter.init(this);
36888     },
36889     
36890     /**
36891      * Gets the minimum size for the resizing element
36892      * @return {Number} The minimum size
36893      */
36894     getMinimumSize : function(){
36895         return this.minSize;
36896     },
36897     
36898     /**
36899      * Sets the minimum size for the resizing element
36900      * @param {Number} minSize The minimum size
36901      */
36902     setMinimumSize : function(minSize){
36903         this.minSize = minSize;
36904     },
36905     
36906     /**
36907      * Gets the maximum size for the resizing element
36908      * @return {Number} The maximum size
36909      */
36910     getMaximumSize : function(){
36911         return this.maxSize;
36912     },
36913     
36914     /**
36915      * Sets the maximum size for the resizing element
36916      * @param {Number} maxSize The maximum size
36917      */
36918     setMaximumSize : function(maxSize){
36919         this.maxSize = maxSize;
36920     },
36921     
36922     /**
36923      * Sets the initialize size for the resizing element
36924      * @param {Number} size The initial size
36925      */
36926     setCurrentSize : function(size){
36927         var oldAnimate = this.animate;
36928         this.animate = false;
36929         this.adapter.setElementSize(this, size);
36930         this.animate = oldAnimate;
36931     },
36932     
36933     /**
36934      * Destroy this splitbar. 
36935      * @param {Boolean} removeEl True to remove the element
36936      */
36937     destroy : function(removeEl){
36938         if(this.shim){
36939             this.shim.remove();
36940         }
36941         this.dd.unreg();
36942         this.proxy.parentNode.removeChild(this.proxy);
36943         if(removeEl){
36944             this.el.remove();
36945         }
36946     }
36947 });
36948
36949 /**
36950  * @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.
36951  */
36952 Roo.bootstrap.SplitBar.createProxy = function(dir){
36953     var proxy = new Roo.Element(document.createElement("div"));
36954     proxy.unselectable();
36955     var cls = 'roo-splitbar-proxy';
36956     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36957     document.body.appendChild(proxy.dom);
36958     return proxy.dom;
36959 };
36960
36961 /** 
36962  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36963  * Default Adapter. It assumes the splitter and resizing element are not positioned
36964  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36965  */
36966 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36967 };
36968
36969 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36970     // do nothing for now
36971     init : function(s){
36972     
36973     },
36974     /**
36975      * Called before drag operations to get the current size of the resizing element. 
36976      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36977      */
36978      getElementSize : function(s){
36979         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36980             return s.resizingEl.getWidth();
36981         }else{
36982             return s.resizingEl.getHeight();
36983         }
36984     },
36985     
36986     /**
36987      * Called after drag operations to set the size of the resizing element.
36988      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36989      * @param {Number} newSize The new size to set
36990      * @param {Function} onComplete A function to be invoked when resizing is complete
36991      */
36992     setElementSize : function(s, newSize, onComplete){
36993         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36994             if(!s.animate){
36995                 s.resizingEl.setWidth(newSize);
36996                 if(onComplete){
36997                     onComplete(s, newSize);
36998                 }
36999             }else{
37000                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37001             }
37002         }else{
37003             
37004             if(!s.animate){
37005                 s.resizingEl.setHeight(newSize);
37006                 if(onComplete){
37007                     onComplete(s, newSize);
37008                 }
37009             }else{
37010                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37011             }
37012         }
37013     }
37014 };
37015
37016 /** 
37017  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37018  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37019  * Adapter that  moves the splitter element to align with the resized sizing element. 
37020  * Used with an absolute positioned SplitBar.
37021  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37022  * document.body, make sure you assign an id to the body element.
37023  */
37024 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37025     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37026     this.container = Roo.get(container);
37027 };
37028
37029 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37030     init : function(s){
37031         this.basic.init(s);
37032     },
37033     
37034     getElementSize : function(s){
37035         return this.basic.getElementSize(s);
37036     },
37037     
37038     setElementSize : function(s, newSize, onComplete){
37039         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37040     },
37041     
37042     moveSplitter : function(s){
37043         var yes = Roo.bootstrap.SplitBar;
37044         switch(s.placement){
37045             case yes.LEFT:
37046                 s.el.setX(s.resizingEl.getRight());
37047                 break;
37048             case yes.RIGHT:
37049                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37050                 break;
37051             case yes.TOP:
37052                 s.el.setY(s.resizingEl.getBottom());
37053                 break;
37054             case yes.BOTTOM:
37055                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37056                 break;
37057         }
37058     }
37059 };
37060
37061 /**
37062  * Orientation constant - Create a vertical SplitBar
37063  * @static
37064  * @type Number
37065  */
37066 Roo.bootstrap.SplitBar.VERTICAL = 1;
37067
37068 /**
37069  * Orientation constant - Create a horizontal SplitBar
37070  * @static
37071  * @type Number
37072  */
37073 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37074
37075 /**
37076  * Placement constant - The resizing element is to the left of the splitter element
37077  * @static
37078  * @type Number
37079  */
37080 Roo.bootstrap.SplitBar.LEFT = 1;
37081
37082 /**
37083  * Placement constant - The resizing element is to the right of the splitter element
37084  * @static
37085  * @type Number
37086  */
37087 Roo.bootstrap.SplitBar.RIGHT = 2;
37088
37089 /**
37090  * Placement constant - The resizing element is positioned above the splitter element
37091  * @static
37092  * @type Number
37093  */
37094 Roo.bootstrap.SplitBar.TOP = 3;
37095
37096 /**
37097  * Placement constant - The resizing element is positioned under splitter element
37098  * @static
37099  * @type Number
37100  */
37101 Roo.bootstrap.SplitBar.BOTTOM = 4;
37102 Roo.namespace("Roo.bootstrap.layout");/*
37103  * Based on:
37104  * Ext JS Library 1.1.1
37105  * Copyright(c) 2006-2007, Ext JS, LLC.
37106  *
37107  * Originally Released Under LGPL - original licence link has changed is not relivant.
37108  *
37109  * Fork - LGPL
37110  * <script type="text/javascript">
37111  */
37112
37113 /**
37114  * @class Roo.bootstrap.layout.Manager
37115  * @extends Roo.bootstrap.Component
37116  * Base class for layout managers.
37117  */
37118 Roo.bootstrap.layout.Manager = function(config)
37119 {
37120     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37121
37122
37123
37124
37125
37126     /** false to disable window resize monitoring @type Boolean */
37127     this.monitorWindowResize = true;
37128     this.regions = {};
37129     this.addEvents({
37130         /**
37131          * @event layout
37132          * Fires when a layout is performed.
37133          * @param {Roo.LayoutManager} this
37134          */
37135         "layout" : true,
37136         /**
37137          * @event regionresized
37138          * Fires when the user resizes a region.
37139          * @param {Roo.LayoutRegion} region The resized region
37140          * @param {Number} newSize The new size (width for east/west, height for north/south)
37141          */
37142         "regionresized" : true,
37143         /**
37144          * @event regioncollapsed
37145          * Fires when a region is collapsed.
37146          * @param {Roo.LayoutRegion} region The collapsed region
37147          */
37148         "regioncollapsed" : true,
37149         /**
37150          * @event regionexpanded
37151          * Fires when a region is expanded.
37152          * @param {Roo.LayoutRegion} region The expanded region
37153          */
37154         "regionexpanded" : true
37155     });
37156     this.updating = false;
37157
37158     if (config.el) {
37159         this.el = Roo.get(config.el);
37160         this.initEvents();
37161     }
37162
37163 };
37164
37165 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37166
37167
37168     regions : null,
37169
37170     monitorWindowResize : true,
37171
37172
37173     updating : false,
37174
37175
37176     onRender : function(ct, position)
37177     {
37178         if(!this.el){
37179             this.el = Roo.get(ct);
37180             this.initEvents();
37181         }
37182         //this.fireEvent('render',this);
37183     },
37184
37185
37186     initEvents: function()
37187     {
37188
37189
37190         // ie scrollbar fix
37191         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37192             document.body.scroll = "no";
37193         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37194             this.el.position('relative');
37195         }
37196         this.id = this.el.id;
37197         this.el.addClass("roo-layout-container");
37198         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37199         if(this.el.dom != document.body ) {
37200             this.el.on('resize', this.layout,this);
37201             this.el.on('show', this.layout,this);
37202         }
37203
37204     },
37205
37206     /**
37207      * Returns true if this layout is currently being updated
37208      * @return {Boolean}
37209      */
37210     isUpdating : function(){
37211         return this.updating;
37212     },
37213
37214     /**
37215      * Suspend the LayoutManager from doing auto-layouts while
37216      * making multiple add or remove calls
37217      */
37218     beginUpdate : function(){
37219         this.updating = true;
37220     },
37221
37222     /**
37223      * Restore auto-layouts and optionally disable the manager from performing a layout
37224      * @param {Boolean} noLayout true to disable a layout update
37225      */
37226     endUpdate : function(noLayout){
37227         this.updating = false;
37228         if(!noLayout){
37229             this.layout();
37230         }
37231     },
37232
37233     layout: function(){
37234         // abstract...
37235     },
37236
37237     onRegionResized : function(region, newSize){
37238         this.fireEvent("regionresized", region, newSize);
37239         this.layout();
37240     },
37241
37242     onRegionCollapsed : function(region){
37243         this.fireEvent("regioncollapsed", region);
37244     },
37245
37246     onRegionExpanded : function(region){
37247         this.fireEvent("regionexpanded", region);
37248     },
37249
37250     /**
37251      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37252      * performs box-model adjustments.
37253      * @return {Object} The size as an object {width: (the width), height: (the height)}
37254      */
37255     getViewSize : function()
37256     {
37257         var size;
37258         if(this.el.dom != document.body){
37259             size = this.el.getSize();
37260         }else{
37261             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37262         }
37263         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37264         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37265         return size;
37266     },
37267
37268     /**
37269      * Returns the Element this layout is bound to.
37270      * @return {Roo.Element}
37271      */
37272     getEl : function(){
37273         return this.el;
37274     },
37275
37276     /**
37277      * Returns the specified region.
37278      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37279      * @return {Roo.LayoutRegion}
37280      */
37281     getRegion : function(target){
37282         return this.regions[target.toLowerCase()];
37283     },
37284
37285     onWindowResize : function(){
37286         if(this.monitorWindowResize){
37287             this.layout();
37288         }
37289     }
37290 });
37291 /*
37292  * Based on:
37293  * Ext JS Library 1.1.1
37294  * Copyright(c) 2006-2007, Ext JS, LLC.
37295  *
37296  * Originally Released Under LGPL - original licence link has changed is not relivant.
37297  *
37298  * Fork - LGPL
37299  * <script type="text/javascript">
37300  */
37301 /**
37302  * @class Roo.bootstrap.layout.Border
37303  * @extends Roo.bootstrap.layout.Manager
37304  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37305  * please see: examples/bootstrap/nested.html<br><br>
37306  
37307 <b>The container the layout is rendered into can be either the body element or any other element.
37308 If it is not the body element, the container needs to either be an absolute positioned element,
37309 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37310 the container size if it is not the body element.</b>
37311
37312 * @constructor
37313 * Create a new Border
37314 * @param {Object} config Configuration options
37315  */
37316 Roo.bootstrap.layout.Border = function(config){
37317     config = config || {};
37318     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37319     
37320     
37321     
37322     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37323         if(config[region]){
37324             config[region].region = region;
37325             this.addRegion(config[region]);
37326         }
37327     },this);
37328     
37329 };
37330
37331 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37332
37333 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37334     
37335     parent : false, // this might point to a 'nest' or a ???
37336     
37337     /**
37338      * Creates and adds a new region if it doesn't already exist.
37339      * @param {String} target The target region key (north, south, east, west or center).
37340      * @param {Object} config The regions config object
37341      * @return {BorderLayoutRegion} The new region
37342      */
37343     addRegion : function(config)
37344     {
37345         if(!this.regions[config.region]){
37346             var r = this.factory(config);
37347             this.bindRegion(r);
37348         }
37349         return this.regions[config.region];
37350     },
37351
37352     // private (kinda)
37353     bindRegion : function(r){
37354         this.regions[r.config.region] = r;
37355         
37356         r.on("visibilitychange",    this.layout, this);
37357         r.on("paneladded",          this.layout, this);
37358         r.on("panelremoved",        this.layout, this);
37359         r.on("invalidated",         this.layout, this);
37360         r.on("resized",             this.onRegionResized, this);
37361         r.on("collapsed",           this.onRegionCollapsed, this);
37362         r.on("expanded",            this.onRegionExpanded, this);
37363     },
37364
37365     /**
37366      * Performs a layout update.
37367      */
37368     layout : function()
37369     {
37370         if(this.updating) {
37371             return;
37372         }
37373         
37374         // render all the rebions if they have not been done alreayd?
37375         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37376             if(this.regions[region] && !this.regions[region].bodyEl){
37377                 this.regions[region].onRender(this.el)
37378             }
37379         },this);
37380         
37381         var size = this.getViewSize();
37382         var w = size.width;
37383         var h = size.height;
37384         var centerW = w;
37385         var centerH = h;
37386         var centerY = 0;
37387         var centerX = 0;
37388         //var x = 0, y = 0;
37389
37390         var rs = this.regions;
37391         var north = rs["north"];
37392         var south = rs["south"]; 
37393         var west = rs["west"];
37394         var east = rs["east"];
37395         var center = rs["center"];
37396         //if(this.hideOnLayout){ // not supported anymore
37397             //c.el.setStyle("display", "none");
37398         //}
37399         if(north && north.isVisible()){
37400             var b = north.getBox();
37401             var m = north.getMargins();
37402             b.width = w - (m.left+m.right);
37403             b.x = m.left;
37404             b.y = m.top;
37405             centerY = b.height + b.y + m.bottom;
37406             centerH -= centerY;
37407             north.updateBox(this.safeBox(b));
37408         }
37409         if(south && south.isVisible()){
37410             var b = south.getBox();
37411             var m = south.getMargins();
37412             b.width = w - (m.left+m.right);
37413             b.x = m.left;
37414             var totalHeight = (b.height + m.top + m.bottom);
37415             b.y = h - totalHeight + m.top;
37416             centerH -= totalHeight;
37417             south.updateBox(this.safeBox(b));
37418         }
37419         if(west && west.isVisible()){
37420             var b = west.getBox();
37421             var m = west.getMargins();
37422             b.height = centerH - (m.top+m.bottom);
37423             b.x = m.left;
37424             b.y = centerY + m.top;
37425             var totalWidth = (b.width + m.left + m.right);
37426             centerX += totalWidth;
37427             centerW -= totalWidth;
37428             west.updateBox(this.safeBox(b));
37429         }
37430         if(east && east.isVisible()){
37431             var b = east.getBox();
37432             var m = east.getMargins();
37433             b.height = centerH - (m.top+m.bottom);
37434             var totalWidth = (b.width + m.left + m.right);
37435             b.x = w - totalWidth + m.left;
37436             b.y = centerY + m.top;
37437             centerW -= totalWidth;
37438             east.updateBox(this.safeBox(b));
37439         }
37440         if(center){
37441             var m = center.getMargins();
37442             var centerBox = {
37443                 x: centerX + m.left,
37444                 y: centerY + m.top,
37445                 width: centerW - (m.left+m.right),
37446                 height: centerH - (m.top+m.bottom)
37447             };
37448             //if(this.hideOnLayout){
37449                 //center.el.setStyle("display", "block");
37450             //}
37451             center.updateBox(this.safeBox(centerBox));
37452         }
37453         this.el.repaint();
37454         this.fireEvent("layout", this);
37455     },
37456
37457     // private
37458     safeBox : function(box){
37459         box.width = Math.max(0, box.width);
37460         box.height = Math.max(0, box.height);
37461         return box;
37462     },
37463
37464     /**
37465      * Adds a ContentPanel (or subclass) to this layout.
37466      * @param {String} target The target region key (north, south, east, west or center).
37467      * @param {Roo.ContentPanel} panel The panel to add
37468      * @return {Roo.ContentPanel} The added panel
37469      */
37470     add : function(target, panel){
37471          
37472         target = target.toLowerCase();
37473         return this.regions[target].add(panel);
37474     },
37475
37476     /**
37477      * Remove a ContentPanel (or subclass) to this layout.
37478      * @param {String} target The target region key (north, south, east, west or center).
37479      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37480      * @return {Roo.ContentPanel} The removed panel
37481      */
37482     remove : function(target, panel){
37483         target = target.toLowerCase();
37484         return this.regions[target].remove(panel);
37485     },
37486
37487     /**
37488      * Searches all regions for a panel with the specified id
37489      * @param {String} panelId
37490      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37491      */
37492     findPanel : function(panelId){
37493         var rs = this.regions;
37494         for(var target in rs){
37495             if(typeof rs[target] != "function"){
37496                 var p = rs[target].getPanel(panelId);
37497                 if(p){
37498                     return p;
37499                 }
37500             }
37501         }
37502         return null;
37503     },
37504
37505     /**
37506      * Searches all regions for a panel with the specified id and activates (shows) it.
37507      * @param {String/ContentPanel} panelId The panels id or the panel itself
37508      * @return {Roo.ContentPanel} The shown panel or null
37509      */
37510     showPanel : function(panelId) {
37511       var rs = this.regions;
37512       for(var target in rs){
37513          var r = rs[target];
37514          if(typeof r != "function"){
37515             if(r.hasPanel(panelId)){
37516                return r.showPanel(panelId);
37517             }
37518          }
37519       }
37520       return null;
37521    },
37522
37523    /**
37524      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37525      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37526      */
37527    /*
37528     restoreState : function(provider){
37529         if(!provider){
37530             provider = Roo.state.Manager;
37531         }
37532         var sm = new Roo.LayoutStateManager();
37533         sm.init(this, provider);
37534     },
37535 */
37536  
37537  
37538     /**
37539      * Adds a xtype elements to the layout.
37540      * <pre><code>
37541
37542 layout.addxtype({
37543        xtype : 'ContentPanel',
37544        region: 'west',
37545        items: [ .... ]
37546    }
37547 );
37548
37549 layout.addxtype({
37550         xtype : 'NestedLayoutPanel',
37551         region: 'west',
37552         layout: {
37553            center: { },
37554            west: { }   
37555         },
37556         items : [ ... list of content panels or nested layout panels.. ]
37557    }
37558 );
37559 </code></pre>
37560      * @param {Object} cfg Xtype definition of item to add.
37561      */
37562     addxtype : function(cfg)
37563     {
37564         // basically accepts a pannel...
37565         // can accept a layout region..!?!?
37566         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37567         
37568         
37569         // theory?  children can only be panels??
37570         
37571         //if (!cfg.xtype.match(/Panel$/)) {
37572         //    return false;
37573         //}
37574         var ret = false;
37575         
37576         if (typeof(cfg.region) == 'undefined') {
37577             Roo.log("Failed to add Panel, region was not set");
37578             Roo.log(cfg);
37579             return false;
37580         }
37581         var region = cfg.region;
37582         delete cfg.region;
37583         
37584           
37585         var xitems = [];
37586         if (cfg.items) {
37587             xitems = cfg.items;
37588             delete cfg.items;
37589         }
37590         var nb = false;
37591         
37592         if ( region == 'center') {
37593             Roo.log("Center: " + cfg.title);
37594         }
37595         
37596         
37597         switch(cfg.xtype) 
37598         {
37599             case 'Content':  // ContentPanel (el, cfg)
37600             case 'Scroll':  // ContentPanel (el, cfg)
37601             case 'View': 
37602                 cfg.autoCreate = cfg.autoCreate || true;
37603                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37604                 //} else {
37605                 //    var el = this.el.createChild();
37606                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37607                 //}
37608                 
37609                 this.add(region, ret);
37610                 break;
37611             
37612             /*
37613             case 'TreePanel': // our new panel!
37614                 cfg.el = this.el.createChild();
37615                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37616                 this.add(region, ret);
37617                 break;
37618             */
37619             
37620             case 'Nest': 
37621                 // create a new Layout (which is  a Border Layout...
37622                 
37623                 var clayout = cfg.layout;
37624                 clayout.el  = this.el.createChild();
37625                 clayout.items   = clayout.items  || [];
37626                 
37627                 delete cfg.layout;
37628                 
37629                 // replace this exitems with the clayout ones..
37630                 xitems = clayout.items;
37631                  
37632                 // force background off if it's in center...
37633                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37634                     cfg.background = false;
37635                 }
37636                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37637                 
37638                 
37639                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37640                 //console.log('adding nested layout panel '  + cfg.toSource());
37641                 this.add(region, ret);
37642                 nb = {}; /// find first...
37643                 break;
37644             
37645             case 'Grid':
37646                 
37647                 // needs grid and region
37648                 
37649                 //var el = this.getRegion(region).el.createChild();
37650                 /*
37651                  *var el = this.el.createChild();
37652                 // create the grid first...
37653                 cfg.grid.container = el;
37654                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37655                 */
37656                 
37657                 if (region == 'center' && this.active ) {
37658                     cfg.background = false;
37659                 }
37660                 
37661                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37662                 
37663                 this.add(region, ret);
37664                 /*
37665                 if (cfg.background) {
37666                     // render grid on panel activation (if panel background)
37667                     ret.on('activate', function(gp) {
37668                         if (!gp.grid.rendered) {
37669                     //        gp.grid.render(el);
37670                         }
37671                     });
37672                 } else {
37673                   //  cfg.grid.render(el);
37674                 }
37675                 */
37676                 break;
37677            
37678            
37679             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37680                 // it was the old xcomponent building that caused this before.
37681                 // espeically if border is the top element in the tree.
37682                 ret = this;
37683                 break; 
37684                 
37685                     
37686                 
37687                 
37688                 
37689             default:
37690                 /*
37691                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37692                     
37693                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37694                     this.add(region, ret);
37695                 } else {
37696                 */
37697                     Roo.log(cfg);
37698                     throw "Can not add '" + cfg.xtype + "' to Border";
37699                     return null;
37700              
37701                                 
37702              
37703         }
37704         this.beginUpdate();
37705         // add children..
37706         var region = '';
37707         var abn = {};
37708         Roo.each(xitems, function(i)  {
37709             region = nb && i.region ? i.region : false;
37710             
37711             var add = ret.addxtype(i);
37712            
37713             if (region) {
37714                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37715                 if (!i.background) {
37716                     abn[region] = nb[region] ;
37717                 }
37718             }
37719             
37720         });
37721         this.endUpdate();
37722
37723         // make the last non-background panel active..
37724         //if (nb) { Roo.log(abn); }
37725         if (nb) {
37726             
37727             for(var r in abn) {
37728                 region = this.getRegion(r);
37729                 if (region) {
37730                     // tried using nb[r], but it does not work..
37731                      
37732                     region.showPanel(abn[r]);
37733                    
37734                 }
37735             }
37736         }
37737         return ret;
37738         
37739     },
37740     
37741     
37742 // private
37743     factory : function(cfg)
37744     {
37745         
37746         var validRegions = Roo.bootstrap.layout.Border.regions;
37747
37748         var target = cfg.region;
37749         cfg.mgr = this;
37750         
37751         var r = Roo.bootstrap.layout;
37752         Roo.log(target);
37753         switch(target){
37754             case "north":
37755                 return new r.North(cfg);
37756             case "south":
37757                 return new r.South(cfg);
37758             case "east":
37759                 return new r.East(cfg);
37760             case "west":
37761                 return new r.West(cfg);
37762             case "center":
37763                 return new r.Center(cfg);
37764         }
37765         throw 'Layout region "'+target+'" not supported.';
37766     }
37767     
37768     
37769 });
37770  /*
37771  * Based on:
37772  * Ext JS Library 1.1.1
37773  * Copyright(c) 2006-2007, Ext JS, LLC.
37774  *
37775  * Originally Released Under LGPL - original licence link has changed is not relivant.
37776  *
37777  * Fork - LGPL
37778  * <script type="text/javascript">
37779  */
37780  
37781 /**
37782  * @class Roo.bootstrap.layout.Basic
37783  * @extends Roo.util.Observable
37784  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37785  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37786  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37787  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37788  * @cfg {string}   region  the region that it inhabits..
37789  * @cfg {bool}   skipConfig skip config?
37790  * 
37791
37792  */
37793 Roo.bootstrap.layout.Basic = function(config){
37794     
37795     this.mgr = config.mgr;
37796     
37797     this.position = config.region;
37798     
37799     var skipConfig = config.skipConfig;
37800     
37801     this.events = {
37802         /**
37803          * @scope Roo.BasicLayoutRegion
37804          */
37805         
37806         /**
37807          * @event beforeremove
37808          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37809          * @param {Roo.LayoutRegion} this
37810          * @param {Roo.ContentPanel} panel The panel
37811          * @param {Object} e The cancel event object
37812          */
37813         "beforeremove" : true,
37814         /**
37815          * @event invalidated
37816          * Fires when the layout for this region is changed.
37817          * @param {Roo.LayoutRegion} this
37818          */
37819         "invalidated" : true,
37820         /**
37821          * @event visibilitychange
37822          * Fires when this region is shown or hidden 
37823          * @param {Roo.LayoutRegion} this
37824          * @param {Boolean} visibility true or false
37825          */
37826         "visibilitychange" : true,
37827         /**
37828          * @event paneladded
37829          * Fires when a panel is added. 
37830          * @param {Roo.LayoutRegion} this
37831          * @param {Roo.ContentPanel} panel The panel
37832          */
37833         "paneladded" : true,
37834         /**
37835          * @event panelremoved
37836          * Fires when a panel is removed. 
37837          * @param {Roo.LayoutRegion} this
37838          * @param {Roo.ContentPanel} panel The panel
37839          */
37840         "panelremoved" : true,
37841         /**
37842          * @event beforecollapse
37843          * Fires when this region before collapse.
37844          * @param {Roo.LayoutRegion} this
37845          */
37846         "beforecollapse" : true,
37847         /**
37848          * @event collapsed
37849          * Fires when this region is collapsed.
37850          * @param {Roo.LayoutRegion} this
37851          */
37852         "collapsed" : true,
37853         /**
37854          * @event expanded
37855          * Fires when this region is expanded.
37856          * @param {Roo.LayoutRegion} this
37857          */
37858         "expanded" : true,
37859         /**
37860          * @event slideshow
37861          * Fires when this region is slid into view.
37862          * @param {Roo.LayoutRegion} this
37863          */
37864         "slideshow" : true,
37865         /**
37866          * @event slidehide
37867          * Fires when this region slides out of view. 
37868          * @param {Roo.LayoutRegion} this
37869          */
37870         "slidehide" : true,
37871         /**
37872          * @event panelactivated
37873          * Fires when a panel is activated. 
37874          * @param {Roo.LayoutRegion} this
37875          * @param {Roo.ContentPanel} panel The activated panel
37876          */
37877         "panelactivated" : true,
37878         /**
37879          * @event resized
37880          * Fires when the user resizes this region. 
37881          * @param {Roo.LayoutRegion} this
37882          * @param {Number} newSize The new size (width for east/west, height for north/south)
37883          */
37884         "resized" : true
37885     };
37886     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37887     this.panels = new Roo.util.MixedCollection();
37888     this.panels.getKey = this.getPanelId.createDelegate(this);
37889     this.box = null;
37890     this.activePanel = null;
37891     // ensure listeners are added...
37892     
37893     if (config.listeners || config.events) {
37894         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37895             listeners : config.listeners || {},
37896             events : config.events || {}
37897         });
37898     }
37899     
37900     if(skipConfig !== true){
37901         this.applyConfig(config);
37902     }
37903 };
37904
37905 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37906 {
37907     getPanelId : function(p){
37908         return p.getId();
37909     },
37910     
37911     applyConfig : function(config){
37912         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37913         this.config = config;
37914         
37915     },
37916     
37917     /**
37918      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37919      * the width, for horizontal (north, south) the height.
37920      * @param {Number} newSize The new width or height
37921      */
37922     resizeTo : function(newSize){
37923         var el = this.el ? this.el :
37924                  (this.activePanel ? this.activePanel.getEl() : null);
37925         if(el){
37926             switch(this.position){
37927                 case "east":
37928                 case "west":
37929                     el.setWidth(newSize);
37930                     this.fireEvent("resized", this, newSize);
37931                 break;
37932                 case "north":
37933                 case "south":
37934                     el.setHeight(newSize);
37935                     this.fireEvent("resized", this, newSize);
37936                 break;                
37937             }
37938         }
37939     },
37940     
37941     getBox : function(){
37942         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37943     },
37944     
37945     getMargins : function(){
37946         return this.margins;
37947     },
37948     
37949     updateBox : function(box){
37950         this.box = box;
37951         var el = this.activePanel.getEl();
37952         el.dom.style.left = box.x + "px";
37953         el.dom.style.top = box.y + "px";
37954         this.activePanel.setSize(box.width, box.height);
37955     },
37956     
37957     /**
37958      * Returns the container element for this region.
37959      * @return {Roo.Element}
37960      */
37961     getEl : function(){
37962         return this.activePanel;
37963     },
37964     
37965     /**
37966      * Returns true if this region is currently visible.
37967      * @return {Boolean}
37968      */
37969     isVisible : function(){
37970         return this.activePanel ? true : false;
37971     },
37972     
37973     setActivePanel : function(panel){
37974         panel = this.getPanel(panel);
37975         if(this.activePanel && this.activePanel != panel){
37976             this.activePanel.setActiveState(false);
37977             this.activePanel.getEl().setLeftTop(-10000,-10000);
37978         }
37979         this.activePanel = panel;
37980         panel.setActiveState(true);
37981         if(this.box){
37982             panel.setSize(this.box.width, this.box.height);
37983         }
37984         this.fireEvent("panelactivated", this, panel);
37985         this.fireEvent("invalidated");
37986     },
37987     
37988     /**
37989      * Show the specified panel.
37990      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37991      * @return {Roo.ContentPanel} The shown panel or null
37992      */
37993     showPanel : function(panel){
37994         panel = this.getPanel(panel);
37995         if(panel){
37996             this.setActivePanel(panel);
37997         }
37998         return panel;
37999     },
38000     
38001     /**
38002      * Get the active panel for this region.
38003      * @return {Roo.ContentPanel} The active panel or null
38004      */
38005     getActivePanel : function(){
38006         return this.activePanel;
38007     },
38008     
38009     /**
38010      * Add the passed ContentPanel(s)
38011      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38012      * @return {Roo.ContentPanel} The panel added (if only one was added)
38013      */
38014     add : function(panel){
38015         if(arguments.length > 1){
38016             for(var i = 0, len = arguments.length; i < len; i++) {
38017                 this.add(arguments[i]);
38018             }
38019             return null;
38020         }
38021         if(this.hasPanel(panel)){
38022             this.showPanel(panel);
38023             return panel;
38024         }
38025         var el = panel.getEl();
38026         if(el.dom.parentNode != this.mgr.el.dom){
38027             this.mgr.el.dom.appendChild(el.dom);
38028         }
38029         if(panel.setRegion){
38030             panel.setRegion(this);
38031         }
38032         this.panels.add(panel);
38033         el.setStyle("position", "absolute");
38034         if(!panel.background){
38035             this.setActivePanel(panel);
38036             if(this.config.initialSize && this.panels.getCount()==1){
38037                 this.resizeTo(this.config.initialSize);
38038             }
38039         }
38040         this.fireEvent("paneladded", this, panel);
38041         return panel;
38042     },
38043     
38044     /**
38045      * Returns true if the panel is in this region.
38046      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38047      * @return {Boolean}
38048      */
38049     hasPanel : function(panel){
38050         if(typeof panel == "object"){ // must be panel obj
38051             panel = panel.getId();
38052         }
38053         return this.getPanel(panel) ? true : false;
38054     },
38055     
38056     /**
38057      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38058      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38059      * @param {Boolean} preservePanel Overrides the config preservePanel option
38060      * @return {Roo.ContentPanel} The panel that was removed
38061      */
38062     remove : function(panel, preservePanel){
38063         panel = this.getPanel(panel);
38064         if(!panel){
38065             return null;
38066         }
38067         var e = {};
38068         this.fireEvent("beforeremove", this, panel, e);
38069         if(e.cancel === true){
38070             return null;
38071         }
38072         var panelId = panel.getId();
38073         this.panels.removeKey(panelId);
38074         return panel;
38075     },
38076     
38077     /**
38078      * Returns the panel specified or null if it's not in this region.
38079      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38080      * @return {Roo.ContentPanel}
38081      */
38082     getPanel : function(id){
38083         if(typeof id == "object"){ // must be panel obj
38084             return id;
38085         }
38086         return this.panels.get(id);
38087     },
38088     
38089     /**
38090      * Returns this regions position (north/south/east/west/center).
38091      * @return {String} 
38092      */
38093     getPosition: function(){
38094         return this.position;    
38095     }
38096 });/*
38097  * Based on:
38098  * Ext JS Library 1.1.1
38099  * Copyright(c) 2006-2007, Ext JS, LLC.
38100  *
38101  * Originally Released Under LGPL - original licence link has changed is not relivant.
38102  *
38103  * Fork - LGPL
38104  * <script type="text/javascript">
38105  */
38106  
38107 /**
38108  * @class Roo.bootstrap.layout.Region
38109  * @extends Roo.bootstrap.layout.Basic
38110  * This class represents a region in a layout manager.
38111  
38112  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38113  * @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})
38114  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38115  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38116  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38117  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38118  * @cfg {String}    title           The title for the region (overrides panel titles)
38119  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38120  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38121  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38122  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38123  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38124  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38125  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38126  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38127  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38128  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38129
38130  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38131  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38132  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38133  * @cfg {Number}    width           For East/West panels
38134  * @cfg {Number}    height          For North/South panels
38135  * @cfg {Boolean}   split           To show the splitter
38136  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38137  * 
38138  * @cfg {string}   cls             Extra CSS classes to add to region
38139  * 
38140  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38141  * @cfg {string}   region  the region that it inhabits..
38142  *
38143
38144  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38145  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38146
38147  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38148  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38149  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38150  */
38151 Roo.bootstrap.layout.Region = function(config)
38152 {
38153     this.applyConfig(config);
38154
38155     var mgr = config.mgr;
38156     var pos = config.region;
38157     config.skipConfig = true;
38158     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38159     
38160     if (mgr.el) {
38161         this.onRender(mgr.el);   
38162     }
38163      
38164     this.visible = true;
38165     this.collapsed = false;
38166     this.unrendered_panels = [];
38167 };
38168
38169 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38170
38171     position: '', // set by wrapper (eg. north/south etc..)
38172     unrendered_panels : null,  // unrendered panels.
38173     
38174     tabPosition : false,
38175     
38176     mgr: false, // points to 'Border'
38177     
38178     
38179     createBody : function(){
38180         /** This region's body element 
38181         * @type Roo.Element */
38182         this.bodyEl = this.el.createChild({
38183                 tag: "div",
38184                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38185         });
38186     },
38187
38188     onRender: function(ctr, pos)
38189     {
38190         var dh = Roo.DomHelper;
38191         /** This region's container element 
38192         * @type Roo.Element */
38193         this.el = dh.append(ctr.dom, {
38194                 tag: "div",
38195                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38196             }, true);
38197         /** This region's title element 
38198         * @type Roo.Element */
38199     
38200         this.titleEl = dh.append(this.el.dom,  {
38201                 tag: "div",
38202                 unselectable: "on",
38203                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38204                 children:[
38205                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38206                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38207                 ]
38208             }, true);
38209         
38210         this.titleEl.enableDisplayMode();
38211         /** This region's title text element 
38212         * @type HTMLElement */
38213         this.titleTextEl = this.titleEl.dom.firstChild;
38214         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38215         /*
38216         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38217         this.closeBtn.enableDisplayMode();
38218         this.closeBtn.on("click", this.closeClicked, this);
38219         this.closeBtn.hide();
38220     */
38221         this.createBody(this.config);
38222         if(this.config.hideWhenEmpty){
38223             this.hide();
38224             this.on("paneladded", this.validateVisibility, this);
38225             this.on("panelremoved", this.validateVisibility, this);
38226         }
38227         if(this.autoScroll){
38228             this.bodyEl.setStyle("overflow", "auto");
38229         }else{
38230             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38231         }
38232         //if(c.titlebar !== false){
38233             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38234                 this.titleEl.hide();
38235             }else{
38236                 this.titleEl.show();
38237                 if(this.config.title){
38238                     this.titleTextEl.innerHTML = this.config.title;
38239                 }
38240             }
38241         //}
38242         if(this.config.collapsed){
38243             this.collapse(true);
38244         }
38245         if(this.config.hidden){
38246             this.hide();
38247         }
38248         
38249         if (this.unrendered_panels && this.unrendered_panels.length) {
38250             for (var i =0;i< this.unrendered_panels.length; i++) {
38251                 this.add(this.unrendered_panels[i]);
38252             }
38253             this.unrendered_panels = null;
38254             
38255         }
38256         
38257     },
38258     
38259     applyConfig : function(c)
38260     {
38261         /*
38262          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38263             var dh = Roo.DomHelper;
38264             if(c.titlebar !== false){
38265                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38266                 this.collapseBtn.on("click", this.collapse, this);
38267                 this.collapseBtn.enableDisplayMode();
38268                 /*
38269                 if(c.showPin === true || this.showPin){
38270                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38271                     this.stickBtn.enableDisplayMode();
38272                     this.stickBtn.on("click", this.expand, this);
38273                     this.stickBtn.hide();
38274                 }
38275                 
38276             }
38277             */
38278             /** This region's collapsed element
38279             * @type Roo.Element */
38280             /*
38281              *
38282             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38283                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38284             ]}, true);
38285             
38286             if(c.floatable !== false){
38287                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38288                this.collapsedEl.on("click", this.collapseClick, this);
38289             }
38290
38291             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38292                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38293                    id: "message", unselectable: "on", style:{"float":"left"}});
38294                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38295              }
38296             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38297             this.expandBtn.on("click", this.expand, this);
38298             
38299         }
38300         
38301         if(this.collapseBtn){
38302             this.collapseBtn.setVisible(c.collapsible == true);
38303         }
38304         
38305         this.cmargins = c.cmargins || this.cmargins ||
38306                          (this.position == "west" || this.position == "east" ?
38307                              {top: 0, left: 2, right:2, bottom: 0} :
38308                              {top: 2, left: 0, right:0, bottom: 2});
38309         */
38310         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38311         
38312         
38313         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38314         
38315         this.autoScroll = c.autoScroll || false;
38316         
38317         
38318        
38319         
38320         this.duration = c.duration || .30;
38321         this.slideDuration = c.slideDuration || .45;
38322         this.config = c;
38323        
38324     },
38325     /**
38326      * Returns true if this region is currently visible.
38327      * @return {Boolean}
38328      */
38329     isVisible : function(){
38330         return this.visible;
38331     },
38332
38333     /**
38334      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38335      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38336      */
38337     //setCollapsedTitle : function(title){
38338     //    title = title || "&#160;";
38339      //   if(this.collapsedTitleTextEl){
38340       //      this.collapsedTitleTextEl.innerHTML = title;
38341        // }
38342     //},
38343
38344     getBox : function(){
38345         var b;
38346       //  if(!this.collapsed){
38347             b = this.el.getBox(false, true);
38348        // }else{
38349           //  b = this.collapsedEl.getBox(false, true);
38350         //}
38351         return b;
38352     },
38353
38354     getMargins : function(){
38355         return this.margins;
38356         //return this.collapsed ? this.cmargins : this.margins;
38357     },
38358 /*
38359     highlight : function(){
38360         this.el.addClass("x-layout-panel-dragover");
38361     },
38362
38363     unhighlight : function(){
38364         this.el.removeClass("x-layout-panel-dragover");
38365     },
38366 */
38367     updateBox : function(box)
38368     {
38369         if (!this.bodyEl) {
38370             return; // not rendered yet..
38371         }
38372         
38373         this.box = box;
38374         if(!this.collapsed){
38375             this.el.dom.style.left = box.x + "px";
38376             this.el.dom.style.top = box.y + "px";
38377             this.updateBody(box.width, box.height);
38378         }else{
38379             this.collapsedEl.dom.style.left = box.x + "px";
38380             this.collapsedEl.dom.style.top = box.y + "px";
38381             this.collapsedEl.setSize(box.width, box.height);
38382         }
38383         if(this.tabs){
38384             this.tabs.autoSizeTabs();
38385         }
38386     },
38387
38388     updateBody : function(w, h)
38389     {
38390         if(w !== null){
38391             this.el.setWidth(w);
38392             w -= this.el.getBorderWidth("rl");
38393             if(this.config.adjustments){
38394                 w += this.config.adjustments[0];
38395             }
38396         }
38397         if(h !== null && h > 0){
38398             this.el.setHeight(h);
38399             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38400             h -= this.el.getBorderWidth("tb");
38401             if(this.config.adjustments){
38402                 h += this.config.adjustments[1];
38403             }
38404             this.bodyEl.setHeight(h);
38405             if(this.tabs){
38406                 h = this.tabs.syncHeight(h);
38407             }
38408         }
38409         if(this.panelSize){
38410             w = w !== null ? w : this.panelSize.width;
38411             h = h !== null ? h : this.panelSize.height;
38412         }
38413         if(this.activePanel){
38414             var el = this.activePanel.getEl();
38415             w = w !== null ? w : el.getWidth();
38416             h = h !== null ? h : el.getHeight();
38417             this.panelSize = {width: w, height: h};
38418             this.activePanel.setSize(w, h);
38419         }
38420         if(Roo.isIE && this.tabs){
38421             this.tabs.el.repaint();
38422         }
38423     },
38424
38425     /**
38426      * Returns the container element for this region.
38427      * @return {Roo.Element}
38428      */
38429     getEl : function(){
38430         return this.el;
38431     },
38432
38433     /**
38434      * Hides this region.
38435      */
38436     hide : function(){
38437         //if(!this.collapsed){
38438             this.el.dom.style.left = "-2000px";
38439             this.el.hide();
38440         //}else{
38441          //   this.collapsedEl.dom.style.left = "-2000px";
38442          //   this.collapsedEl.hide();
38443        // }
38444         this.visible = false;
38445         this.fireEvent("visibilitychange", this, false);
38446     },
38447
38448     /**
38449      * Shows this region if it was previously hidden.
38450      */
38451     show : function(){
38452         //if(!this.collapsed){
38453             this.el.show();
38454         //}else{
38455         //    this.collapsedEl.show();
38456        // }
38457         this.visible = true;
38458         this.fireEvent("visibilitychange", this, true);
38459     },
38460 /*
38461     closeClicked : function(){
38462         if(this.activePanel){
38463             this.remove(this.activePanel);
38464         }
38465     },
38466
38467     collapseClick : function(e){
38468         if(this.isSlid){
38469            e.stopPropagation();
38470            this.slideIn();
38471         }else{
38472            e.stopPropagation();
38473            this.slideOut();
38474         }
38475     },
38476 */
38477     /**
38478      * Collapses this region.
38479      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38480      */
38481     /*
38482     collapse : function(skipAnim, skipCheck = false){
38483         if(this.collapsed) {
38484             return;
38485         }
38486         
38487         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38488             
38489             this.collapsed = true;
38490             if(this.split){
38491                 this.split.el.hide();
38492             }
38493             if(this.config.animate && skipAnim !== true){
38494                 this.fireEvent("invalidated", this);
38495                 this.animateCollapse();
38496             }else{
38497                 this.el.setLocation(-20000,-20000);
38498                 this.el.hide();
38499                 this.collapsedEl.show();
38500                 this.fireEvent("collapsed", this);
38501                 this.fireEvent("invalidated", this);
38502             }
38503         }
38504         
38505     },
38506 */
38507     animateCollapse : function(){
38508         // overridden
38509     },
38510
38511     /**
38512      * Expands this region if it was previously collapsed.
38513      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38514      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38515      */
38516     /*
38517     expand : function(e, skipAnim){
38518         if(e) {
38519             e.stopPropagation();
38520         }
38521         if(!this.collapsed || this.el.hasActiveFx()) {
38522             return;
38523         }
38524         if(this.isSlid){
38525             this.afterSlideIn();
38526             skipAnim = true;
38527         }
38528         this.collapsed = false;
38529         if(this.config.animate && skipAnim !== true){
38530             this.animateExpand();
38531         }else{
38532             this.el.show();
38533             if(this.split){
38534                 this.split.el.show();
38535             }
38536             this.collapsedEl.setLocation(-2000,-2000);
38537             this.collapsedEl.hide();
38538             this.fireEvent("invalidated", this);
38539             this.fireEvent("expanded", this);
38540         }
38541     },
38542 */
38543     animateExpand : function(){
38544         // overridden
38545     },
38546
38547     initTabs : function()
38548     {
38549         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38550         
38551         var ts = new Roo.bootstrap.panel.Tabs({
38552             el: this.bodyEl.dom,
38553             region : this,
38554             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38555             disableTooltips: this.config.disableTabTips,
38556             toolbar : this.config.toolbar
38557         });
38558         
38559         if(this.config.hideTabs){
38560             ts.stripWrap.setDisplayed(false);
38561         }
38562         this.tabs = ts;
38563         ts.resizeTabs = this.config.resizeTabs === true;
38564         ts.minTabWidth = this.config.minTabWidth || 40;
38565         ts.maxTabWidth = this.config.maxTabWidth || 250;
38566         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38567         ts.monitorResize = false;
38568         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38569         ts.bodyEl.addClass('roo-layout-tabs-body');
38570         this.panels.each(this.initPanelAsTab, this);
38571     },
38572
38573     initPanelAsTab : function(panel){
38574         var ti = this.tabs.addTab(
38575             panel.getEl().id,
38576             panel.getTitle(),
38577             null,
38578             this.config.closeOnTab && panel.isClosable(),
38579             panel.tpl
38580         );
38581         if(panel.tabTip !== undefined){
38582             ti.setTooltip(panel.tabTip);
38583         }
38584         ti.on("activate", function(){
38585               this.setActivePanel(panel);
38586         }, this);
38587         
38588         if(this.config.closeOnTab){
38589             ti.on("beforeclose", function(t, e){
38590                 e.cancel = true;
38591                 this.remove(panel);
38592             }, this);
38593         }
38594         
38595         panel.tabItem = ti;
38596         
38597         return ti;
38598     },
38599
38600     updatePanelTitle : function(panel, title)
38601     {
38602         if(this.activePanel == panel){
38603             this.updateTitle(title);
38604         }
38605         if(this.tabs){
38606             var ti = this.tabs.getTab(panel.getEl().id);
38607             ti.setText(title);
38608             if(panel.tabTip !== undefined){
38609                 ti.setTooltip(panel.tabTip);
38610             }
38611         }
38612     },
38613
38614     updateTitle : function(title){
38615         if(this.titleTextEl && !this.config.title){
38616             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38617         }
38618     },
38619
38620     setActivePanel : function(panel)
38621     {
38622         panel = this.getPanel(panel);
38623         if(this.activePanel && this.activePanel != panel){
38624             if(this.activePanel.setActiveState(false) === false){
38625                 return;
38626             }
38627         }
38628         this.activePanel = panel;
38629         panel.setActiveState(true);
38630         if(this.panelSize){
38631             panel.setSize(this.panelSize.width, this.panelSize.height);
38632         }
38633         if(this.closeBtn){
38634             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38635         }
38636         this.updateTitle(panel.getTitle());
38637         if(this.tabs){
38638             this.fireEvent("invalidated", this);
38639         }
38640         this.fireEvent("panelactivated", this, panel);
38641     },
38642
38643     /**
38644      * Shows the specified panel.
38645      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38646      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38647      */
38648     showPanel : function(panel)
38649     {
38650         panel = this.getPanel(panel);
38651         if(panel){
38652             if(this.tabs){
38653                 var tab = this.tabs.getTab(panel.getEl().id);
38654                 if(tab.isHidden()){
38655                     this.tabs.unhideTab(tab.id);
38656                 }
38657                 tab.activate();
38658             }else{
38659                 this.setActivePanel(panel);
38660             }
38661         }
38662         return panel;
38663     },
38664
38665     /**
38666      * Get the active panel for this region.
38667      * @return {Roo.ContentPanel} The active panel or null
38668      */
38669     getActivePanel : function(){
38670         return this.activePanel;
38671     },
38672
38673     validateVisibility : function(){
38674         if(this.panels.getCount() < 1){
38675             this.updateTitle("&#160;");
38676             this.closeBtn.hide();
38677             this.hide();
38678         }else{
38679             if(!this.isVisible()){
38680                 this.show();
38681             }
38682         }
38683     },
38684
38685     /**
38686      * Adds the passed ContentPanel(s) to this region.
38687      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38688      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38689      */
38690     add : function(panel)
38691     {
38692         if(arguments.length > 1){
38693             for(var i = 0, len = arguments.length; i < len; i++) {
38694                 this.add(arguments[i]);
38695             }
38696             return null;
38697         }
38698         
38699         // if we have not been rendered yet, then we can not really do much of this..
38700         if (!this.bodyEl) {
38701             this.unrendered_panels.push(panel);
38702             return panel;
38703         }
38704         
38705         
38706         
38707         
38708         if(this.hasPanel(panel)){
38709             this.showPanel(panel);
38710             return panel;
38711         }
38712         panel.setRegion(this);
38713         this.panels.add(panel);
38714        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38715             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38716             // and hide them... ???
38717             this.bodyEl.dom.appendChild(panel.getEl().dom);
38718             if(panel.background !== true){
38719                 this.setActivePanel(panel);
38720             }
38721             this.fireEvent("paneladded", this, panel);
38722             return panel;
38723         }
38724         */
38725         if(!this.tabs){
38726             this.initTabs();
38727         }else{
38728             this.initPanelAsTab(panel);
38729         }
38730         
38731         
38732         if(panel.background !== true){
38733             this.tabs.activate(panel.getEl().id);
38734         }
38735         this.fireEvent("paneladded", this, panel);
38736         return panel;
38737     },
38738
38739     /**
38740      * Hides the tab for the specified panel.
38741      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38742      */
38743     hidePanel : function(panel){
38744         if(this.tabs && (panel = this.getPanel(panel))){
38745             this.tabs.hideTab(panel.getEl().id);
38746         }
38747     },
38748
38749     /**
38750      * Unhides the tab for a previously hidden panel.
38751      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38752      */
38753     unhidePanel : function(panel){
38754         if(this.tabs && (panel = this.getPanel(panel))){
38755             this.tabs.unhideTab(panel.getEl().id);
38756         }
38757     },
38758
38759     clearPanels : function(){
38760         while(this.panels.getCount() > 0){
38761              this.remove(this.panels.first());
38762         }
38763     },
38764
38765     /**
38766      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38767      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38768      * @param {Boolean} preservePanel Overrides the config preservePanel option
38769      * @return {Roo.ContentPanel} The panel that was removed
38770      */
38771     remove : function(panel, preservePanel)
38772     {
38773         panel = this.getPanel(panel);
38774         if(!panel){
38775             return null;
38776         }
38777         var e = {};
38778         this.fireEvent("beforeremove", this, panel, e);
38779         if(e.cancel === true){
38780             return null;
38781         }
38782         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38783         var panelId = panel.getId();
38784         this.panels.removeKey(panelId);
38785         if(preservePanel){
38786             document.body.appendChild(panel.getEl().dom);
38787         }
38788         if(this.tabs){
38789             this.tabs.removeTab(panel.getEl().id);
38790         }else if (!preservePanel){
38791             this.bodyEl.dom.removeChild(panel.getEl().dom);
38792         }
38793         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38794             var p = this.panels.first();
38795             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38796             tempEl.appendChild(p.getEl().dom);
38797             this.bodyEl.update("");
38798             this.bodyEl.dom.appendChild(p.getEl().dom);
38799             tempEl = null;
38800             this.updateTitle(p.getTitle());
38801             this.tabs = null;
38802             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38803             this.setActivePanel(p);
38804         }
38805         panel.setRegion(null);
38806         if(this.activePanel == panel){
38807             this.activePanel = null;
38808         }
38809         if(this.config.autoDestroy !== false && preservePanel !== true){
38810             try{panel.destroy();}catch(e){}
38811         }
38812         this.fireEvent("panelremoved", this, panel);
38813         return panel;
38814     },
38815
38816     /**
38817      * Returns the TabPanel component used by this region
38818      * @return {Roo.TabPanel}
38819      */
38820     getTabs : function(){
38821         return this.tabs;
38822     },
38823
38824     createTool : function(parentEl, className){
38825         var btn = Roo.DomHelper.append(parentEl, {
38826             tag: "div",
38827             cls: "x-layout-tools-button",
38828             children: [ {
38829                 tag: "div",
38830                 cls: "roo-layout-tools-button-inner " + className,
38831                 html: "&#160;"
38832             }]
38833         }, true);
38834         btn.addClassOnOver("roo-layout-tools-button-over");
38835         return btn;
38836     }
38837 });/*
38838  * Based on:
38839  * Ext JS Library 1.1.1
38840  * Copyright(c) 2006-2007, Ext JS, LLC.
38841  *
38842  * Originally Released Under LGPL - original licence link has changed is not relivant.
38843  *
38844  * Fork - LGPL
38845  * <script type="text/javascript">
38846  */
38847  
38848
38849
38850 /**
38851  * @class Roo.SplitLayoutRegion
38852  * @extends Roo.LayoutRegion
38853  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38854  */
38855 Roo.bootstrap.layout.Split = function(config){
38856     this.cursor = config.cursor;
38857     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38858 };
38859
38860 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38861 {
38862     splitTip : "Drag to resize.",
38863     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38864     useSplitTips : false,
38865
38866     applyConfig : function(config){
38867         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38868     },
38869     
38870     onRender : function(ctr,pos) {
38871         
38872         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38873         if(!this.config.split){
38874             return;
38875         }
38876         if(!this.split){
38877             
38878             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38879                             tag: "div",
38880                             id: this.el.id + "-split",
38881                             cls: "roo-layout-split roo-layout-split-"+this.position,
38882                             html: "&#160;"
38883             });
38884             /** The SplitBar for this region 
38885             * @type Roo.SplitBar */
38886             // does not exist yet...
38887             Roo.log([this.position, this.orientation]);
38888             
38889             this.split = new Roo.bootstrap.SplitBar({
38890                 dragElement : splitEl,
38891                 resizingElement: this.el,
38892                 orientation : this.orientation
38893             });
38894             
38895             this.split.on("moved", this.onSplitMove, this);
38896             this.split.useShim = this.config.useShim === true;
38897             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38898             if(this.useSplitTips){
38899                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38900             }
38901             //if(config.collapsible){
38902             //    this.split.el.on("dblclick", this.collapse,  this);
38903             //}
38904         }
38905         if(typeof this.config.minSize != "undefined"){
38906             this.split.minSize = this.config.minSize;
38907         }
38908         if(typeof this.config.maxSize != "undefined"){
38909             this.split.maxSize = this.config.maxSize;
38910         }
38911         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38912             this.hideSplitter();
38913         }
38914         
38915     },
38916
38917     getHMaxSize : function(){
38918          var cmax = this.config.maxSize || 10000;
38919          var center = this.mgr.getRegion("center");
38920          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38921     },
38922
38923     getVMaxSize : function(){
38924          var cmax = this.config.maxSize || 10000;
38925          var center = this.mgr.getRegion("center");
38926          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38927     },
38928
38929     onSplitMove : function(split, newSize){
38930         this.fireEvent("resized", this, newSize);
38931     },
38932     
38933     /** 
38934      * Returns the {@link Roo.SplitBar} for this region.
38935      * @return {Roo.SplitBar}
38936      */
38937     getSplitBar : function(){
38938         return this.split;
38939     },
38940     
38941     hide : function(){
38942         this.hideSplitter();
38943         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38944     },
38945
38946     hideSplitter : function(){
38947         if(this.split){
38948             this.split.el.setLocation(-2000,-2000);
38949             this.split.el.hide();
38950         }
38951     },
38952
38953     show : function(){
38954         if(this.split){
38955             this.split.el.show();
38956         }
38957         Roo.bootstrap.layout.Split.superclass.show.call(this);
38958     },
38959     
38960     beforeSlide: function(){
38961         if(Roo.isGecko){// firefox overflow auto bug workaround
38962             this.bodyEl.clip();
38963             if(this.tabs) {
38964                 this.tabs.bodyEl.clip();
38965             }
38966             if(this.activePanel){
38967                 this.activePanel.getEl().clip();
38968                 
38969                 if(this.activePanel.beforeSlide){
38970                     this.activePanel.beforeSlide();
38971                 }
38972             }
38973         }
38974     },
38975     
38976     afterSlide : function(){
38977         if(Roo.isGecko){// firefox overflow auto bug workaround
38978             this.bodyEl.unclip();
38979             if(this.tabs) {
38980                 this.tabs.bodyEl.unclip();
38981             }
38982             if(this.activePanel){
38983                 this.activePanel.getEl().unclip();
38984                 if(this.activePanel.afterSlide){
38985                     this.activePanel.afterSlide();
38986                 }
38987             }
38988         }
38989     },
38990
38991     initAutoHide : function(){
38992         if(this.autoHide !== false){
38993             if(!this.autoHideHd){
38994                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38995                 this.autoHideHd = {
38996                     "mouseout": function(e){
38997                         if(!e.within(this.el, true)){
38998                             st.delay(500);
38999                         }
39000                     },
39001                     "mouseover" : function(e){
39002                         st.cancel();
39003                     },
39004                     scope : this
39005                 };
39006             }
39007             this.el.on(this.autoHideHd);
39008         }
39009     },
39010
39011     clearAutoHide : function(){
39012         if(this.autoHide !== false){
39013             this.el.un("mouseout", this.autoHideHd.mouseout);
39014             this.el.un("mouseover", this.autoHideHd.mouseover);
39015         }
39016     },
39017
39018     clearMonitor : function(){
39019         Roo.get(document).un("click", this.slideInIf, this);
39020     },
39021
39022     // these names are backwards but not changed for compat
39023     slideOut : function(){
39024         if(this.isSlid || this.el.hasActiveFx()){
39025             return;
39026         }
39027         this.isSlid = true;
39028         if(this.collapseBtn){
39029             this.collapseBtn.hide();
39030         }
39031         this.closeBtnState = this.closeBtn.getStyle('display');
39032         this.closeBtn.hide();
39033         if(this.stickBtn){
39034             this.stickBtn.show();
39035         }
39036         this.el.show();
39037         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39038         this.beforeSlide();
39039         this.el.setStyle("z-index", 10001);
39040         this.el.slideIn(this.getSlideAnchor(), {
39041             callback: function(){
39042                 this.afterSlide();
39043                 this.initAutoHide();
39044                 Roo.get(document).on("click", this.slideInIf, this);
39045                 this.fireEvent("slideshow", this);
39046             },
39047             scope: this,
39048             block: true
39049         });
39050     },
39051
39052     afterSlideIn : function(){
39053         this.clearAutoHide();
39054         this.isSlid = false;
39055         this.clearMonitor();
39056         this.el.setStyle("z-index", "");
39057         if(this.collapseBtn){
39058             this.collapseBtn.show();
39059         }
39060         this.closeBtn.setStyle('display', this.closeBtnState);
39061         if(this.stickBtn){
39062             this.stickBtn.hide();
39063         }
39064         this.fireEvent("slidehide", this);
39065     },
39066
39067     slideIn : function(cb){
39068         if(!this.isSlid || this.el.hasActiveFx()){
39069             Roo.callback(cb);
39070             return;
39071         }
39072         this.isSlid = false;
39073         this.beforeSlide();
39074         this.el.slideOut(this.getSlideAnchor(), {
39075             callback: function(){
39076                 this.el.setLeftTop(-10000, -10000);
39077                 this.afterSlide();
39078                 this.afterSlideIn();
39079                 Roo.callback(cb);
39080             },
39081             scope: this,
39082             block: true
39083         });
39084     },
39085     
39086     slideInIf : function(e){
39087         if(!e.within(this.el)){
39088             this.slideIn();
39089         }
39090     },
39091
39092     animateCollapse : function(){
39093         this.beforeSlide();
39094         this.el.setStyle("z-index", 20000);
39095         var anchor = this.getSlideAnchor();
39096         this.el.slideOut(anchor, {
39097             callback : function(){
39098                 this.el.setStyle("z-index", "");
39099                 this.collapsedEl.slideIn(anchor, {duration:.3});
39100                 this.afterSlide();
39101                 this.el.setLocation(-10000,-10000);
39102                 this.el.hide();
39103                 this.fireEvent("collapsed", this);
39104             },
39105             scope: this,
39106             block: true
39107         });
39108     },
39109
39110     animateExpand : function(){
39111         this.beforeSlide();
39112         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39113         this.el.setStyle("z-index", 20000);
39114         this.collapsedEl.hide({
39115             duration:.1
39116         });
39117         this.el.slideIn(this.getSlideAnchor(), {
39118             callback : function(){
39119                 this.el.setStyle("z-index", "");
39120                 this.afterSlide();
39121                 if(this.split){
39122                     this.split.el.show();
39123                 }
39124                 this.fireEvent("invalidated", this);
39125                 this.fireEvent("expanded", this);
39126             },
39127             scope: this,
39128             block: true
39129         });
39130     },
39131
39132     anchors : {
39133         "west" : "left",
39134         "east" : "right",
39135         "north" : "top",
39136         "south" : "bottom"
39137     },
39138
39139     sanchors : {
39140         "west" : "l",
39141         "east" : "r",
39142         "north" : "t",
39143         "south" : "b"
39144     },
39145
39146     canchors : {
39147         "west" : "tl-tr",
39148         "east" : "tr-tl",
39149         "north" : "tl-bl",
39150         "south" : "bl-tl"
39151     },
39152
39153     getAnchor : function(){
39154         return this.anchors[this.position];
39155     },
39156
39157     getCollapseAnchor : function(){
39158         return this.canchors[this.position];
39159     },
39160
39161     getSlideAnchor : function(){
39162         return this.sanchors[this.position];
39163     },
39164
39165     getAlignAdj : function(){
39166         var cm = this.cmargins;
39167         switch(this.position){
39168             case "west":
39169                 return [0, 0];
39170             break;
39171             case "east":
39172                 return [0, 0];
39173             break;
39174             case "north":
39175                 return [0, 0];
39176             break;
39177             case "south":
39178                 return [0, 0];
39179             break;
39180         }
39181     },
39182
39183     getExpandAdj : function(){
39184         var c = this.collapsedEl, cm = this.cmargins;
39185         switch(this.position){
39186             case "west":
39187                 return [-(cm.right+c.getWidth()+cm.left), 0];
39188             break;
39189             case "east":
39190                 return [cm.right+c.getWidth()+cm.left, 0];
39191             break;
39192             case "north":
39193                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39194             break;
39195             case "south":
39196                 return [0, cm.top+cm.bottom+c.getHeight()];
39197             break;
39198         }
39199     }
39200 });/*
39201  * Based on:
39202  * Ext JS Library 1.1.1
39203  * Copyright(c) 2006-2007, Ext JS, LLC.
39204  *
39205  * Originally Released Under LGPL - original licence link has changed is not relivant.
39206  *
39207  * Fork - LGPL
39208  * <script type="text/javascript">
39209  */
39210 /*
39211  * These classes are private internal classes
39212  */
39213 Roo.bootstrap.layout.Center = function(config){
39214     config.region = "center";
39215     Roo.bootstrap.layout.Region.call(this, config);
39216     this.visible = true;
39217     this.minWidth = config.minWidth || 20;
39218     this.minHeight = config.minHeight || 20;
39219 };
39220
39221 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39222     hide : function(){
39223         // center panel can't be hidden
39224     },
39225     
39226     show : function(){
39227         // center panel can't be hidden
39228     },
39229     
39230     getMinWidth: function(){
39231         return this.minWidth;
39232     },
39233     
39234     getMinHeight: function(){
39235         return this.minHeight;
39236     }
39237 });
39238
39239
39240
39241
39242  
39243
39244
39245
39246
39247
39248
39249 Roo.bootstrap.layout.North = function(config)
39250 {
39251     config.region = 'north';
39252     config.cursor = 'n-resize';
39253     
39254     Roo.bootstrap.layout.Split.call(this, config);
39255     
39256     
39257     if(this.split){
39258         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39259         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39260         this.split.el.addClass("roo-layout-split-v");
39261     }
39262     var size = config.initialSize || config.height;
39263     if(typeof size != "undefined"){
39264         this.el.setHeight(size);
39265     }
39266 };
39267 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39268 {
39269     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39270     
39271     
39272     
39273     getBox : function(){
39274         if(this.collapsed){
39275             return this.collapsedEl.getBox();
39276         }
39277         var box = this.el.getBox();
39278         if(this.split){
39279             box.height += this.split.el.getHeight();
39280         }
39281         return box;
39282     },
39283     
39284     updateBox : function(box){
39285         if(this.split && !this.collapsed){
39286             box.height -= this.split.el.getHeight();
39287             this.split.el.setLeft(box.x);
39288             this.split.el.setTop(box.y+box.height);
39289             this.split.el.setWidth(box.width);
39290         }
39291         if(this.collapsed){
39292             this.updateBody(box.width, null);
39293         }
39294         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39295     }
39296 });
39297
39298
39299
39300
39301
39302 Roo.bootstrap.layout.South = function(config){
39303     config.region = 'south';
39304     config.cursor = 's-resize';
39305     Roo.bootstrap.layout.Split.call(this, config);
39306     if(this.split){
39307         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39308         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39309         this.split.el.addClass("roo-layout-split-v");
39310     }
39311     var size = config.initialSize || config.height;
39312     if(typeof size != "undefined"){
39313         this.el.setHeight(size);
39314     }
39315 };
39316
39317 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39318     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39319     getBox : function(){
39320         if(this.collapsed){
39321             return this.collapsedEl.getBox();
39322         }
39323         var box = this.el.getBox();
39324         if(this.split){
39325             var sh = this.split.el.getHeight();
39326             box.height += sh;
39327             box.y -= sh;
39328         }
39329         return box;
39330     },
39331     
39332     updateBox : function(box){
39333         if(this.split && !this.collapsed){
39334             var sh = this.split.el.getHeight();
39335             box.height -= sh;
39336             box.y += sh;
39337             this.split.el.setLeft(box.x);
39338             this.split.el.setTop(box.y-sh);
39339             this.split.el.setWidth(box.width);
39340         }
39341         if(this.collapsed){
39342             this.updateBody(box.width, null);
39343         }
39344         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39345     }
39346 });
39347
39348 Roo.bootstrap.layout.East = function(config){
39349     config.region = "east";
39350     config.cursor = "e-resize";
39351     Roo.bootstrap.layout.Split.call(this, config);
39352     if(this.split){
39353         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39354         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39355         this.split.el.addClass("roo-layout-split-h");
39356     }
39357     var size = config.initialSize || config.width;
39358     if(typeof size != "undefined"){
39359         this.el.setWidth(size);
39360     }
39361 };
39362 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39363     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39364     getBox : function(){
39365         if(this.collapsed){
39366             return this.collapsedEl.getBox();
39367         }
39368         var box = this.el.getBox();
39369         if(this.split){
39370             var sw = this.split.el.getWidth();
39371             box.width += sw;
39372             box.x -= sw;
39373         }
39374         return box;
39375     },
39376
39377     updateBox : function(box){
39378         if(this.split && !this.collapsed){
39379             var sw = this.split.el.getWidth();
39380             box.width -= sw;
39381             this.split.el.setLeft(box.x);
39382             this.split.el.setTop(box.y);
39383             this.split.el.setHeight(box.height);
39384             box.x += sw;
39385         }
39386         if(this.collapsed){
39387             this.updateBody(null, box.height);
39388         }
39389         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39390     }
39391 });
39392
39393 Roo.bootstrap.layout.West = function(config){
39394     config.region = "west";
39395     config.cursor = "w-resize";
39396     
39397     Roo.bootstrap.layout.Split.call(this, config);
39398     if(this.split){
39399         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39400         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39401         this.split.el.addClass("roo-layout-split-h");
39402     }
39403     
39404 };
39405 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39406     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39407     
39408     onRender: function(ctr, pos)
39409     {
39410         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39411         var size = this.config.initialSize || this.config.width;
39412         if(typeof size != "undefined"){
39413             this.el.setWidth(size);
39414         }
39415     },
39416     
39417     getBox : function(){
39418         if(this.collapsed){
39419             return this.collapsedEl.getBox();
39420         }
39421         var box = this.el.getBox();
39422         if(this.split){
39423             box.width += this.split.el.getWidth();
39424         }
39425         return box;
39426     },
39427     
39428     updateBox : function(box){
39429         if(this.split && !this.collapsed){
39430             var sw = this.split.el.getWidth();
39431             box.width -= sw;
39432             this.split.el.setLeft(box.x+box.width);
39433             this.split.el.setTop(box.y);
39434             this.split.el.setHeight(box.height);
39435         }
39436         if(this.collapsed){
39437             this.updateBody(null, box.height);
39438         }
39439         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39440     }
39441 });Roo.namespace("Roo.bootstrap.panel");/*
39442  * Based on:
39443  * Ext JS Library 1.1.1
39444  * Copyright(c) 2006-2007, Ext JS, LLC.
39445  *
39446  * Originally Released Under LGPL - original licence link has changed is not relivant.
39447  *
39448  * Fork - LGPL
39449  * <script type="text/javascript">
39450  */
39451 /**
39452  * @class Roo.ContentPanel
39453  * @extends Roo.util.Observable
39454  * A basic ContentPanel element.
39455  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39456  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39457  * @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
39458  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39459  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39460  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39461  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39462  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39463  * @cfg {String} title          The title for this panel
39464  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39465  * @cfg {String} url            Calls {@link #setUrl} with this value
39466  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39467  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39468  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39469  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39470  * @cfg {Boolean} badges render the badges
39471  * @cfg {String} cls  extra classes to use  
39472  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39473
39474  * @constructor
39475  * Create a new ContentPanel.
39476  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39477  * @param {String/Object} config A string to set only the title or a config object
39478  * @param {String} content (optional) Set the HTML content for this panel
39479  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39480  */
39481 Roo.bootstrap.panel.Content = function( config){
39482     
39483     this.tpl = config.tpl || false;
39484     
39485     var el = config.el;
39486     var content = config.content;
39487
39488     if(config.autoCreate){ // xtype is available if this is called from factory
39489         el = Roo.id();
39490     }
39491     this.el = Roo.get(el);
39492     if(!this.el && config && config.autoCreate){
39493         if(typeof config.autoCreate == "object"){
39494             if(!config.autoCreate.id){
39495                 config.autoCreate.id = config.id||el;
39496             }
39497             this.el = Roo.DomHelper.append(document.body,
39498                         config.autoCreate, true);
39499         }else{
39500             var elcfg =  {
39501                 tag: "div",
39502                 cls: (config.cls || '') +
39503                     (config.background ? ' bg-' + config.background : '') +
39504                     " roo-layout-inactive-content",
39505                 id: config.id||el
39506             };
39507             if (config.html) {
39508                 elcfg.html = config.html;
39509                 
39510             }
39511                         
39512             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39513         }
39514     } 
39515     this.closable = false;
39516     this.loaded = false;
39517     this.active = false;
39518    
39519       
39520     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39521         
39522         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39523         
39524         this.wrapEl = this.el; //this.el.wrap();
39525         var ti = [];
39526         if (config.toolbar.items) {
39527             ti = config.toolbar.items ;
39528             delete config.toolbar.items ;
39529         }
39530         
39531         var nitems = [];
39532         this.toolbar.render(this.wrapEl, 'before');
39533         for(var i =0;i < ti.length;i++) {
39534           //  Roo.log(['add child', items[i]]);
39535             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39536         }
39537         this.toolbar.items = nitems;
39538         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39539         delete config.toolbar;
39540         
39541     }
39542     /*
39543     // xtype created footer. - not sure if will work as we normally have to render first..
39544     if (this.footer && !this.footer.el && this.footer.xtype) {
39545         if (!this.wrapEl) {
39546             this.wrapEl = this.el.wrap();
39547         }
39548     
39549         this.footer.container = this.wrapEl.createChild();
39550          
39551         this.footer = Roo.factory(this.footer, Roo);
39552         
39553     }
39554     */
39555     
39556      if(typeof config == "string"){
39557         this.title = config;
39558     }else{
39559         Roo.apply(this, config);
39560     }
39561     
39562     if(this.resizeEl){
39563         this.resizeEl = Roo.get(this.resizeEl, true);
39564     }else{
39565         this.resizeEl = this.el;
39566     }
39567     // handle view.xtype
39568     
39569  
39570     
39571     
39572     this.addEvents({
39573         /**
39574          * @event activate
39575          * Fires when this panel is activated. 
39576          * @param {Roo.ContentPanel} this
39577          */
39578         "activate" : true,
39579         /**
39580          * @event deactivate
39581          * Fires when this panel is activated. 
39582          * @param {Roo.ContentPanel} this
39583          */
39584         "deactivate" : true,
39585
39586         /**
39587          * @event resize
39588          * Fires when this panel is resized if fitToFrame is true.
39589          * @param {Roo.ContentPanel} this
39590          * @param {Number} width The width after any component adjustments
39591          * @param {Number} height The height after any component adjustments
39592          */
39593         "resize" : true,
39594         
39595          /**
39596          * @event render
39597          * Fires when this tab is created
39598          * @param {Roo.ContentPanel} this
39599          */
39600         "render" : true
39601         
39602         
39603         
39604     });
39605     
39606
39607     
39608     
39609     if(this.autoScroll){
39610         this.resizeEl.setStyle("overflow", "auto");
39611     } else {
39612         // fix randome scrolling
39613         //this.el.on('scroll', function() {
39614         //    Roo.log('fix random scolling');
39615         //    this.scrollTo('top',0); 
39616         //});
39617     }
39618     content = content || this.content;
39619     if(content){
39620         this.setContent(content);
39621     }
39622     if(config && config.url){
39623         this.setUrl(this.url, this.params, this.loadOnce);
39624     }
39625     
39626     
39627     
39628     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39629     
39630     if (this.view && typeof(this.view.xtype) != 'undefined') {
39631         this.view.el = this.el.appendChild(document.createElement("div"));
39632         this.view = Roo.factory(this.view); 
39633         this.view.render  &&  this.view.render(false, '');  
39634     }
39635     
39636     
39637     this.fireEvent('render', this);
39638 };
39639
39640 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39641     
39642     cls : '',
39643     background : '',
39644     
39645     tabTip : '',
39646     
39647     setRegion : function(region){
39648         this.region = region;
39649         this.setActiveClass(region && !this.background);
39650     },
39651     
39652     
39653     setActiveClass: function(state)
39654     {
39655         if(state){
39656            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39657            this.el.setStyle('position','relative');
39658         }else{
39659            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39660            this.el.setStyle('position', 'absolute');
39661         } 
39662     },
39663     
39664     /**
39665      * Returns the toolbar for this Panel if one was configured. 
39666      * @return {Roo.Toolbar} 
39667      */
39668     getToolbar : function(){
39669         return this.toolbar;
39670     },
39671     
39672     setActiveState : function(active)
39673     {
39674         this.active = active;
39675         this.setActiveClass(active);
39676         if(!active){
39677             if(this.fireEvent("deactivate", this) === false){
39678                 return false;
39679             }
39680             return true;
39681         }
39682         this.fireEvent("activate", this);
39683         return true;
39684     },
39685     /**
39686      * Updates this panel's element
39687      * @param {String} content The new content
39688      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39689     */
39690     setContent : function(content, loadScripts){
39691         this.el.update(content, loadScripts);
39692     },
39693
39694     ignoreResize : function(w, h){
39695         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39696             return true;
39697         }else{
39698             this.lastSize = {width: w, height: h};
39699             return false;
39700         }
39701     },
39702     /**
39703      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39704      * @return {Roo.UpdateManager} The UpdateManager
39705      */
39706     getUpdateManager : function(){
39707         return this.el.getUpdateManager();
39708     },
39709      /**
39710      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39711      * @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:
39712 <pre><code>
39713 panel.load({
39714     url: "your-url.php",
39715     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39716     callback: yourFunction,
39717     scope: yourObject, //(optional scope)
39718     discardUrl: false,
39719     nocache: false,
39720     text: "Loading...",
39721     timeout: 30,
39722     scripts: false
39723 });
39724 </code></pre>
39725      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39726      * 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.
39727      * @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}
39728      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39729      * @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.
39730      * @return {Roo.ContentPanel} this
39731      */
39732     load : function(){
39733         var um = this.el.getUpdateManager();
39734         um.update.apply(um, arguments);
39735         return this;
39736     },
39737
39738
39739     /**
39740      * 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.
39741      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39742      * @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)
39743      * @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)
39744      * @return {Roo.UpdateManager} The UpdateManager
39745      */
39746     setUrl : function(url, params, loadOnce){
39747         if(this.refreshDelegate){
39748             this.removeListener("activate", this.refreshDelegate);
39749         }
39750         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39751         this.on("activate", this.refreshDelegate);
39752         return this.el.getUpdateManager();
39753     },
39754     
39755     _handleRefresh : function(url, params, loadOnce){
39756         if(!loadOnce || !this.loaded){
39757             var updater = this.el.getUpdateManager();
39758             updater.update(url, params, this._setLoaded.createDelegate(this));
39759         }
39760     },
39761     
39762     _setLoaded : function(){
39763         this.loaded = true;
39764     }, 
39765     
39766     /**
39767      * Returns this panel's id
39768      * @return {String} 
39769      */
39770     getId : function(){
39771         return this.el.id;
39772     },
39773     
39774     /** 
39775      * Returns this panel's element - used by regiosn to add.
39776      * @return {Roo.Element} 
39777      */
39778     getEl : function(){
39779         return this.wrapEl || this.el;
39780     },
39781     
39782    
39783     
39784     adjustForComponents : function(width, height)
39785     {
39786         //Roo.log('adjustForComponents ');
39787         if(this.resizeEl != this.el){
39788             width -= this.el.getFrameWidth('lr');
39789             height -= this.el.getFrameWidth('tb');
39790         }
39791         if(this.toolbar){
39792             var te = this.toolbar.getEl();
39793             te.setWidth(width);
39794             height -= te.getHeight();
39795         }
39796         if(this.footer){
39797             var te = this.footer.getEl();
39798             te.setWidth(width);
39799             height -= te.getHeight();
39800         }
39801         
39802         
39803         if(this.adjustments){
39804             width += this.adjustments[0];
39805             height += this.adjustments[1];
39806         }
39807         return {"width": width, "height": height};
39808     },
39809     
39810     setSize : function(width, height){
39811         if(this.fitToFrame && !this.ignoreResize(width, height)){
39812             if(this.fitContainer && this.resizeEl != this.el){
39813                 this.el.setSize(width, height);
39814             }
39815             var size = this.adjustForComponents(width, height);
39816             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39817             this.fireEvent('resize', this, size.width, size.height);
39818         }
39819     },
39820     
39821     /**
39822      * Returns this panel's title
39823      * @return {String} 
39824      */
39825     getTitle : function(){
39826         
39827         if (typeof(this.title) != 'object') {
39828             return this.title;
39829         }
39830         
39831         var t = '';
39832         for (var k in this.title) {
39833             if (!this.title.hasOwnProperty(k)) {
39834                 continue;
39835             }
39836             
39837             if (k.indexOf('-') >= 0) {
39838                 var s = k.split('-');
39839                 for (var i = 0; i<s.length; i++) {
39840                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39841                 }
39842             } else {
39843                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39844             }
39845         }
39846         return t;
39847     },
39848     
39849     /**
39850      * Set this panel's title
39851      * @param {String} title
39852      */
39853     setTitle : function(title){
39854         this.title = title;
39855         if(this.region){
39856             this.region.updatePanelTitle(this, title);
39857         }
39858     },
39859     
39860     /**
39861      * Returns true is this panel was configured to be closable
39862      * @return {Boolean} 
39863      */
39864     isClosable : function(){
39865         return this.closable;
39866     },
39867     
39868     beforeSlide : function(){
39869         this.el.clip();
39870         this.resizeEl.clip();
39871     },
39872     
39873     afterSlide : function(){
39874         this.el.unclip();
39875         this.resizeEl.unclip();
39876     },
39877     
39878     /**
39879      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39880      *   Will fail silently if the {@link #setUrl} method has not been called.
39881      *   This does not activate the panel, just updates its content.
39882      */
39883     refresh : function(){
39884         if(this.refreshDelegate){
39885            this.loaded = false;
39886            this.refreshDelegate();
39887         }
39888     },
39889     
39890     /**
39891      * Destroys this panel
39892      */
39893     destroy : function(){
39894         this.el.removeAllListeners();
39895         var tempEl = document.createElement("span");
39896         tempEl.appendChild(this.el.dom);
39897         tempEl.innerHTML = "";
39898         this.el.remove();
39899         this.el = null;
39900     },
39901     
39902     /**
39903      * form - if the content panel contains a form - this is a reference to it.
39904      * @type {Roo.form.Form}
39905      */
39906     form : false,
39907     /**
39908      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39909      *    This contains a reference to it.
39910      * @type {Roo.View}
39911      */
39912     view : false,
39913     
39914       /**
39915      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39916      * <pre><code>
39917
39918 layout.addxtype({
39919        xtype : 'Form',
39920        items: [ .... ]
39921    }
39922 );
39923
39924 </code></pre>
39925      * @param {Object} cfg Xtype definition of item to add.
39926      */
39927     
39928     
39929     getChildContainer: function () {
39930         return this.getEl();
39931     }
39932     
39933     
39934     /*
39935         var  ret = new Roo.factory(cfg);
39936         return ret;
39937         
39938         
39939         // add form..
39940         if (cfg.xtype.match(/^Form$/)) {
39941             
39942             var el;
39943             //if (this.footer) {
39944             //    el = this.footer.container.insertSibling(false, 'before');
39945             //} else {
39946                 el = this.el.createChild();
39947             //}
39948
39949             this.form = new  Roo.form.Form(cfg);
39950             
39951             
39952             if ( this.form.allItems.length) {
39953                 this.form.render(el.dom);
39954             }
39955             return this.form;
39956         }
39957         // should only have one of theses..
39958         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39959             // views.. should not be just added - used named prop 'view''
39960             
39961             cfg.el = this.el.appendChild(document.createElement("div"));
39962             // factory?
39963             
39964             var ret = new Roo.factory(cfg);
39965              
39966              ret.render && ret.render(false, ''); // render blank..
39967             this.view = ret;
39968             return ret;
39969         }
39970         return false;
39971     }
39972     \*/
39973 });
39974  
39975 /**
39976  * @class Roo.bootstrap.panel.Grid
39977  * @extends Roo.bootstrap.panel.Content
39978  * @constructor
39979  * Create a new GridPanel.
39980  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39981  * @param {Object} config A the config object
39982   
39983  */
39984
39985
39986
39987 Roo.bootstrap.panel.Grid = function(config)
39988 {
39989     
39990       
39991     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39992         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39993
39994     config.el = this.wrapper;
39995     //this.el = this.wrapper;
39996     
39997       if (config.container) {
39998         // ctor'ed from a Border/panel.grid
39999         
40000         
40001         this.wrapper.setStyle("overflow", "hidden");
40002         this.wrapper.addClass('roo-grid-container');
40003
40004     }
40005     
40006     
40007     if(config.toolbar){
40008         var tool_el = this.wrapper.createChild();    
40009         this.toolbar = Roo.factory(config.toolbar);
40010         var ti = [];
40011         if (config.toolbar.items) {
40012             ti = config.toolbar.items ;
40013             delete config.toolbar.items ;
40014         }
40015         
40016         var nitems = [];
40017         this.toolbar.render(tool_el);
40018         for(var i =0;i < ti.length;i++) {
40019           //  Roo.log(['add child', items[i]]);
40020             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40021         }
40022         this.toolbar.items = nitems;
40023         
40024         delete config.toolbar;
40025     }
40026     
40027     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40028     config.grid.scrollBody = true;;
40029     config.grid.monitorWindowResize = false; // turn off autosizing
40030     config.grid.autoHeight = false;
40031     config.grid.autoWidth = false;
40032     
40033     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40034     
40035     if (config.background) {
40036         // render grid on panel activation (if panel background)
40037         this.on('activate', function(gp) {
40038             if (!gp.grid.rendered) {
40039                 gp.grid.render(this.wrapper);
40040                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40041             }
40042         });
40043             
40044     } else {
40045         this.grid.render(this.wrapper);
40046         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40047
40048     }
40049     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40050     // ??? needed ??? config.el = this.wrapper;
40051     
40052     
40053     
40054   
40055     // xtype created footer. - not sure if will work as we normally have to render first..
40056     if (this.footer && !this.footer.el && this.footer.xtype) {
40057         
40058         var ctr = this.grid.getView().getFooterPanel(true);
40059         this.footer.dataSource = this.grid.dataSource;
40060         this.footer = Roo.factory(this.footer, Roo);
40061         this.footer.render(ctr);
40062         
40063     }
40064     
40065     
40066     
40067     
40068      
40069 };
40070
40071 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40072     getId : function(){
40073         return this.grid.id;
40074     },
40075     
40076     /**
40077      * Returns the grid for this panel
40078      * @return {Roo.bootstrap.Table} 
40079      */
40080     getGrid : function(){
40081         return this.grid;    
40082     },
40083     
40084     setSize : function(width, height){
40085         if(!this.ignoreResize(width, height)){
40086             var grid = this.grid;
40087             var size = this.adjustForComponents(width, height);
40088             // tfoot is not a footer?
40089           
40090             
40091             var gridel = grid.getGridEl();
40092             gridel.setSize(size.width, size.height);
40093             
40094             var tbd = grid.getGridEl().select('tbody', true).first();
40095             var thd = grid.getGridEl().select('thead',true).first();
40096             var tbf= grid.getGridEl().select('tfoot', true).first();
40097
40098             if (tbf) {
40099                 size.height -= thd.getHeight();
40100             }
40101             if (thd) {
40102                 size.height -= thd.getHeight();
40103             }
40104             
40105             tbd.setSize(size.width, size.height );
40106             // this is for the account management tab -seems to work there.
40107             var thd = grid.getGridEl().select('thead',true).first();
40108             //if (tbd) {
40109             //    tbd.setSize(size.width, size.height - thd.getHeight());
40110             //}
40111              
40112             grid.autoSize();
40113         }
40114     },
40115      
40116     
40117     
40118     beforeSlide : function(){
40119         this.grid.getView().scroller.clip();
40120     },
40121     
40122     afterSlide : function(){
40123         this.grid.getView().scroller.unclip();
40124     },
40125     
40126     destroy : function(){
40127         this.grid.destroy();
40128         delete this.grid;
40129         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40130     }
40131 });
40132
40133 /**
40134  * @class Roo.bootstrap.panel.Nest
40135  * @extends Roo.bootstrap.panel.Content
40136  * @constructor
40137  * Create a new Panel, that can contain a layout.Border.
40138  * 
40139  * 
40140  * @param {Roo.BorderLayout} layout The layout for this panel
40141  * @param {String/Object} config A string to set only the title or a config object
40142  */
40143 Roo.bootstrap.panel.Nest = function(config)
40144 {
40145     // construct with only one argument..
40146     /* FIXME - implement nicer consturctors
40147     if (layout.layout) {
40148         config = layout;
40149         layout = config.layout;
40150         delete config.layout;
40151     }
40152     if (layout.xtype && !layout.getEl) {
40153         // then layout needs constructing..
40154         layout = Roo.factory(layout, Roo);
40155     }
40156     */
40157     
40158     config.el =  config.layout.getEl();
40159     
40160     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40161     
40162     config.layout.monitorWindowResize = false; // turn off autosizing
40163     this.layout = config.layout;
40164     this.layout.getEl().addClass("roo-layout-nested-layout");
40165     this.layout.parent = this;
40166     
40167     
40168     
40169     
40170 };
40171
40172 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40173
40174     setSize : function(width, height){
40175         if(!this.ignoreResize(width, height)){
40176             var size = this.adjustForComponents(width, height);
40177             var el = this.layout.getEl();
40178             if (size.height < 1) {
40179                 el.setWidth(size.width);   
40180             } else {
40181                 el.setSize(size.width, size.height);
40182             }
40183             var touch = el.dom.offsetWidth;
40184             this.layout.layout();
40185             // ie requires a double layout on the first pass
40186             if(Roo.isIE && !this.initialized){
40187                 this.initialized = true;
40188                 this.layout.layout();
40189             }
40190         }
40191     },
40192     
40193     // activate all subpanels if not currently active..
40194     
40195     setActiveState : function(active){
40196         this.active = active;
40197         this.setActiveClass(active);
40198         
40199         if(!active){
40200             this.fireEvent("deactivate", this);
40201             return;
40202         }
40203         
40204         this.fireEvent("activate", this);
40205         // not sure if this should happen before or after..
40206         if (!this.layout) {
40207             return; // should not happen..
40208         }
40209         var reg = false;
40210         for (var r in this.layout.regions) {
40211             reg = this.layout.getRegion(r);
40212             if (reg.getActivePanel()) {
40213                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40214                 reg.setActivePanel(reg.getActivePanel());
40215                 continue;
40216             }
40217             if (!reg.panels.length) {
40218                 continue;
40219             }
40220             reg.showPanel(reg.getPanel(0));
40221         }
40222         
40223         
40224         
40225         
40226     },
40227     
40228     /**
40229      * Returns the nested BorderLayout for this panel
40230      * @return {Roo.BorderLayout} 
40231      */
40232     getLayout : function(){
40233         return this.layout;
40234     },
40235     
40236      /**
40237      * Adds a xtype elements to the layout of the nested panel
40238      * <pre><code>
40239
40240 panel.addxtype({
40241        xtype : 'ContentPanel',
40242        region: 'west',
40243        items: [ .... ]
40244    }
40245 );
40246
40247 panel.addxtype({
40248         xtype : 'NestedLayoutPanel',
40249         region: 'west',
40250         layout: {
40251            center: { },
40252            west: { }   
40253         },
40254         items : [ ... list of content panels or nested layout panels.. ]
40255    }
40256 );
40257 </code></pre>
40258      * @param {Object} cfg Xtype definition of item to add.
40259      */
40260     addxtype : function(cfg) {
40261         return this.layout.addxtype(cfg);
40262     
40263     }
40264 });/*
40265  * Based on:
40266  * Ext JS Library 1.1.1
40267  * Copyright(c) 2006-2007, Ext JS, LLC.
40268  *
40269  * Originally Released Under LGPL - original licence link has changed is not relivant.
40270  *
40271  * Fork - LGPL
40272  * <script type="text/javascript">
40273  */
40274 /**
40275  * @class Roo.TabPanel
40276  * @extends Roo.util.Observable
40277  * A lightweight tab container.
40278  * <br><br>
40279  * Usage:
40280  * <pre><code>
40281 // basic tabs 1, built from existing content
40282 var tabs = new Roo.TabPanel("tabs1");
40283 tabs.addTab("script", "View Script");
40284 tabs.addTab("markup", "View Markup");
40285 tabs.activate("script");
40286
40287 // more advanced tabs, built from javascript
40288 var jtabs = new Roo.TabPanel("jtabs");
40289 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40290
40291 // set up the UpdateManager
40292 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40293 var updater = tab2.getUpdateManager();
40294 updater.setDefaultUrl("ajax1.htm");
40295 tab2.on('activate', updater.refresh, updater, true);
40296
40297 // Use setUrl for Ajax loading
40298 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40299 tab3.setUrl("ajax2.htm", null, true);
40300
40301 // Disabled tab
40302 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40303 tab4.disable();
40304
40305 jtabs.activate("jtabs-1");
40306  * </code></pre>
40307  * @constructor
40308  * Create a new TabPanel.
40309  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40310  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40311  */
40312 Roo.bootstrap.panel.Tabs = function(config){
40313     /**
40314     * The container element for this TabPanel.
40315     * @type Roo.Element
40316     */
40317     this.el = Roo.get(config.el);
40318     delete config.el;
40319     if(config){
40320         if(typeof config == "boolean"){
40321             this.tabPosition = config ? "bottom" : "top";
40322         }else{
40323             Roo.apply(this, config);
40324         }
40325     }
40326     
40327     if(this.tabPosition == "bottom"){
40328         // if tabs are at the bottom = create the body first.
40329         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40330         this.el.addClass("roo-tabs-bottom");
40331     }
40332     // next create the tabs holders
40333     
40334     if (this.tabPosition == "west"){
40335         
40336         var reg = this.region; // fake it..
40337         while (reg) {
40338             if (!reg.mgr.parent) {
40339                 break;
40340             }
40341             reg = reg.mgr.parent.region;
40342         }
40343         Roo.log("got nest?");
40344         Roo.log(reg);
40345         if (reg.mgr.getRegion('west')) {
40346             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40347             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40348             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40349             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40350             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40351         
40352             
40353         }
40354         
40355         
40356     } else {
40357      
40358         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40359         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40360         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40361         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40362     }
40363     
40364     
40365     if(Roo.isIE){
40366         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40367     }
40368     
40369     // finally - if tabs are at the top, then create the body last..
40370     if(this.tabPosition != "bottom"){
40371         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40372          * @type Roo.Element
40373          */
40374         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40375         this.el.addClass("roo-tabs-top");
40376     }
40377     this.items = [];
40378
40379     this.bodyEl.setStyle("position", "relative");
40380
40381     this.active = null;
40382     this.activateDelegate = this.activate.createDelegate(this);
40383
40384     this.addEvents({
40385         /**
40386          * @event tabchange
40387          * Fires when the active tab changes
40388          * @param {Roo.TabPanel} this
40389          * @param {Roo.TabPanelItem} activePanel The new active tab
40390          */
40391         "tabchange": true,
40392         /**
40393          * @event beforetabchange
40394          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40395          * @param {Roo.TabPanel} this
40396          * @param {Object} e Set cancel to true on this object to cancel the tab change
40397          * @param {Roo.TabPanelItem} tab The tab being changed to
40398          */
40399         "beforetabchange" : true
40400     });
40401
40402     Roo.EventManager.onWindowResize(this.onResize, this);
40403     this.cpad = this.el.getPadding("lr");
40404     this.hiddenCount = 0;
40405
40406
40407     // toolbar on the tabbar support...
40408     if (this.toolbar) {
40409         alert("no toolbar support yet");
40410         this.toolbar  = false;
40411         /*
40412         var tcfg = this.toolbar;
40413         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40414         this.toolbar = new Roo.Toolbar(tcfg);
40415         if (Roo.isSafari) {
40416             var tbl = tcfg.container.child('table', true);
40417             tbl.setAttribute('width', '100%');
40418         }
40419         */
40420         
40421     }
40422    
40423
40424
40425     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40426 };
40427
40428 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40429     /*
40430      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40431      */
40432     tabPosition : "top",
40433     /*
40434      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40435      */
40436     currentTabWidth : 0,
40437     /*
40438      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40439      */
40440     minTabWidth : 40,
40441     /*
40442      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40443      */
40444     maxTabWidth : 250,
40445     /*
40446      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40447      */
40448     preferredTabWidth : 175,
40449     /*
40450      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40451      */
40452     resizeTabs : false,
40453     /*
40454      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40455      */
40456     monitorResize : true,
40457     /*
40458      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40459      */
40460     toolbar : false,  // set by caller..
40461     
40462     region : false, /// set by caller
40463     
40464     disableTooltips : true, // not used yet...
40465
40466     /**
40467      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40468      * @param {String} id The id of the div to use <b>or create</b>
40469      * @param {String} text The text for the tab
40470      * @param {String} content (optional) Content to put in the TabPanelItem body
40471      * @param {Boolean} closable (optional) True to create a close icon on the tab
40472      * @return {Roo.TabPanelItem} The created TabPanelItem
40473      */
40474     addTab : function(id, text, content, closable, tpl)
40475     {
40476         var item = new Roo.bootstrap.panel.TabItem({
40477             panel: this,
40478             id : id,
40479             text : text,
40480             closable : closable,
40481             tpl : tpl
40482         });
40483         this.addTabItem(item);
40484         if(content){
40485             item.setContent(content);
40486         }
40487         return item;
40488     },
40489
40490     /**
40491      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40492      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40493      * @return {Roo.TabPanelItem}
40494      */
40495     getTab : function(id){
40496         return this.items[id];
40497     },
40498
40499     /**
40500      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40501      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40502      */
40503     hideTab : function(id){
40504         var t = this.items[id];
40505         if(!t.isHidden()){
40506            t.setHidden(true);
40507            this.hiddenCount++;
40508            this.autoSizeTabs();
40509         }
40510     },
40511
40512     /**
40513      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40514      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40515      */
40516     unhideTab : function(id){
40517         var t = this.items[id];
40518         if(t.isHidden()){
40519            t.setHidden(false);
40520            this.hiddenCount--;
40521            this.autoSizeTabs();
40522         }
40523     },
40524
40525     /**
40526      * Adds an existing {@link Roo.TabPanelItem}.
40527      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40528      */
40529     addTabItem : function(item)
40530     {
40531         this.items[item.id] = item;
40532         this.items.push(item);
40533         this.autoSizeTabs();
40534       //  if(this.resizeTabs){
40535     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40536   //         this.autoSizeTabs();
40537 //        }else{
40538 //            item.autoSize();
40539        // }
40540     },
40541
40542     /**
40543      * Removes a {@link Roo.TabPanelItem}.
40544      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40545      */
40546     removeTab : function(id){
40547         var items = this.items;
40548         var tab = items[id];
40549         if(!tab) { return; }
40550         var index = items.indexOf(tab);
40551         if(this.active == tab && items.length > 1){
40552             var newTab = this.getNextAvailable(index);
40553             if(newTab) {
40554                 newTab.activate();
40555             }
40556         }
40557         this.stripEl.dom.removeChild(tab.pnode.dom);
40558         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40559             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40560         }
40561         items.splice(index, 1);
40562         delete this.items[tab.id];
40563         tab.fireEvent("close", tab);
40564         tab.purgeListeners();
40565         this.autoSizeTabs();
40566     },
40567
40568     getNextAvailable : function(start){
40569         var items = this.items;
40570         var index = start;
40571         // look for a next tab that will slide over to
40572         // replace the one being removed
40573         while(index < items.length){
40574             var item = items[++index];
40575             if(item && !item.isHidden()){
40576                 return item;
40577             }
40578         }
40579         // if one isn't found select the previous tab (on the left)
40580         index = start;
40581         while(index >= 0){
40582             var item = items[--index];
40583             if(item && !item.isHidden()){
40584                 return item;
40585             }
40586         }
40587         return null;
40588     },
40589
40590     /**
40591      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40592      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40593      */
40594     disableTab : function(id){
40595         var tab = this.items[id];
40596         if(tab && this.active != tab){
40597             tab.disable();
40598         }
40599     },
40600
40601     /**
40602      * Enables a {@link Roo.TabPanelItem} that is disabled.
40603      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40604      */
40605     enableTab : function(id){
40606         var tab = this.items[id];
40607         tab.enable();
40608     },
40609
40610     /**
40611      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40612      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40613      * @return {Roo.TabPanelItem} The TabPanelItem.
40614      */
40615     activate : function(id)
40616     {
40617         //Roo.log('activite:'  + id);
40618         
40619         var tab = this.items[id];
40620         if(!tab){
40621             return null;
40622         }
40623         if(tab == this.active || tab.disabled){
40624             return tab;
40625         }
40626         var e = {};
40627         this.fireEvent("beforetabchange", this, e, tab);
40628         if(e.cancel !== true && !tab.disabled){
40629             if(this.active){
40630                 this.active.hide();
40631             }
40632             this.active = this.items[id];
40633             this.active.show();
40634             this.fireEvent("tabchange", this, this.active);
40635         }
40636         return tab;
40637     },
40638
40639     /**
40640      * Gets the active {@link Roo.TabPanelItem}.
40641      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40642      */
40643     getActiveTab : function(){
40644         return this.active;
40645     },
40646
40647     /**
40648      * Updates the tab body element to fit the height of the container element
40649      * for overflow scrolling
40650      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40651      */
40652     syncHeight : function(targetHeight){
40653         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40654         var bm = this.bodyEl.getMargins();
40655         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40656         this.bodyEl.setHeight(newHeight);
40657         return newHeight;
40658     },
40659
40660     onResize : function(){
40661         if(this.monitorResize){
40662             this.autoSizeTabs();
40663         }
40664     },
40665
40666     /**
40667      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40668      */
40669     beginUpdate : function(){
40670         this.updating = true;
40671     },
40672
40673     /**
40674      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40675      */
40676     endUpdate : function(){
40677         this.updating = false;
40678         this.autoSizeTabs();
40679     },
40680
40681     /**
40682      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40683      */
40684     autoSizeTabs : function()
40685     {
40686         var count = this.items.length;
40687         var vcount = count - this.hiddenCount;
40688         
40689         if (vcount < 2) {
40690             this.stripEl.hide();
40691         } else {
40692             this.stripEl.show();
40693         }
40694         
40695         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40696             return;
40697         }
40698         
40699         
40700         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40701         var availWidth = Math.floor(w / vcount);
40702         var b = this.stripBody;
40703         if(b.getWidth() > w){
40704             var tabs = this.items;
40705             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40706             if(availWidth < this.minTabWidth){
40707                 /*if(!this.sleft){    // incomplete scrolling code
40708                     this.createScrollButtons();
40709                 }
40710                 this.showScroll();
40711                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40712             }
40713         }else{
40714             if(this.currentTabWidth < this.preferredTabWidth){
40715                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40716             }
40717         }
40718     },
40719
40720     /**
40721      * Returns the number of tabs in this TabPanel.
40722      * @return {Number}
40723      */
40724      getCount : function(){
40725          return this.items.length;
40726      },
40727
40728     /**
40729      * Resizes all the tabs to the passed width
40730      * @param {Number} The new width
40731      */
40732     setTabWidth : function(width){
40733         this.currentTabWidth = width;
40734         for(var i = 0, len = this.items.length; i < len; i++) {
40735                 if(!this.items[i].isHidden()) {
40736                 this.items[i].setWidth(width);
40737             }
40738         }
40739     },
40740
40741     /**
40742      * Destroys this TabPanel
40743      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40744      */
40745     destroy : function(removeEl){
40746         Roo.EventManager.removeResizeListener(this.onResize, this);
40747         for(var i = 0, len = this.items.length; i < len; i++){
40748             this.items[i].purgeListeners();
40749         }
40750         if(removeEl === true){
40751             this.el.update("");
40752             this.el.remove();
40753         }
40754     },
40755     
40756     createStrip : function(container)
40757     {
40758         var strip = document.createElement("nav");
40759         strip.className = Roo.bootstrap.version == 4 ?
40760             "navbar-light bg-light" : 
40761             "navbar navbar-default"; //"x-tabs-wrap";
40762         container.appendChild(strip);
40763         return strip;
40764     },
40765     
40766     createStripList : function(strip)
40767     {
40768         // div wrapper for retard IE
40769         // returns the "tr" element.
40770         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40771         //'<div class="x-tabs-strip-wrap">'+
40772           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40773           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40774         return strip.firstChild; //.firstChild.firstChild.firstChild;
40775     },
40776     createBody : function(container)
40777     {
40778         var body = document.createElement("div");
40779         Roo.id(body, "tab-body");
40780         //Roo.fly(body).addClass("x-tabs-body");
40781         Roo.fly(body).addClass("tab-content");
40782         container.appendChild(body);
40783         return body;
40784     },
40785     createItemBody :function(bodyEl, id){
40786         var body = Roo.getDom(id);
40787         if(!body){
40788             body = document.createElement("div");
40789             body.id = id;
40790         }
40791         //Roo.fly(body).addClass("x-tabs-item-body");
40792         Roo.fly(body).addClass("tab-pane");
40793          bodyEl.insertBefore(body, bodyEl.firstChild);
40794         return body;
40795     },
40796     /** @private */
40797     createStripElements :  function(stripEl, text, closable, tpl)
40798     {
40799         var td = document.createElement("li"); // was td..
40800         td.className = 'nav-item';
40801         
40802         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40803         
40804         
40805         stripEl.appendChild(td);
40806         /*if(closable){
40807             td.className = "x-tabs-closable";
40808             if(!this.closeTpl){
40809                 this.closeTpl = new Roo.Template(
40810                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40811                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40812                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40813                 );
40814             }
40815             var el = this.closeTpl.overwrite(td, {"text": text});
40816             var close = el.getElementsByTagName("div")[0];
40817             var inner = el.getElementsByTagName("em")[0];
40818             return {"el": el, "close": close, "inner": inner};
40819         } else {
40820         */
40821         // not sure what this is..
40822 //            if(!this.tabTpl){
40823                 //this.tabTpl = new Roo.Template(
40824                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40825                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40826                 //);
40827 //                this.tabTpl = new Roo.Template(
40828 //                   '<a href="#">' +
40829 //                   '<span unselectable="on"' +
40830 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40831 //                            ' >{text}</span></a>'
40832 //                );
40833 //                
40834 //            }
40835
40836
40837             var template = tpl || this.tabTpl || false;
40838             
40839             if(!template){
40840                 template =  new Roo.Template(
40841                         Roo.bootstrap.version == 4 ? 
40842                             (
40843                                 '<a class="nav-link" href="#" unselectable="on"' +
40844                                      (this.disableTooltips ? '' : ' title="{text}"') +
40845                                      ' >{text}</a>'
40846                             ) : (
40847                                 '<a class="nav-link" href="#">' +
40848                                 '<span unselectable="on"' +
40849                                          (this.disableTooltips ? '' : ' title="{text}"') +
40850                                     ' >{text}</span></a>'
40851                             )
40852                 );
40853             }
40854             
40855             switch (typeof(template)) {
40856                 case 'object' :
40857                     break;
40858                 case 'string' :
40859                     template = new Roo.Template(template);
40860                     break;
40861                 default :
40862                     break;
40863             }
40864             
40865             var el = template.overwrite(td, {"text": text});
40866             
40867             var inner = el.getElementsByTagName("span")[0];
40868             
40869             return {"el": el, "inner": inner};
40870             
40871     }
40872         
40873     
40874 });
40875
40876 /**
40877  * @class Roo.TabPanelItem
40878  * @extends Roo.util.Observable
40879  * Represents an individual item (tab plus body) in a TabPanel.
40880  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40881  * @param {String} id The id of this TabPanelItem
40882  * @param {String} text The text for the tab of this TabPanelItem
40883  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40884  */
40885 Roo.bootstrap.panel.TabItem = function(config){
40886     /**
40887      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40888      * @type Roo.TabPanel
40889      */
40890     this.tabPanel = config.panel;
40891     /**
40892      * The id for this TabPanelItem
40893      * @type String
40894      */
40895     this.id = config.id;
40896     /** @private */
40897     this.disabled = false;
40898     /** @private */
40899     this.text = config.text;
40900     /** @private */
40901     this.loaded = false;
40902     this.closable = config.closable;
40903
40904     /**
40905      * The body element for this TabPanelItem.
40906      * @type Roo.Element
40907      */
40908     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40909     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40910     this.bodyEl.setStyle("display", "block");
40911     this.bodyEl.setStyle("zoom", "1");
40912     //this.hideAction();
40913
40914     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40915     /** @private */
40916     this.el = Roo.get(els.el);
40917     this.inner = Roo.get(els.inner, true);
40918      this.textEl = Roo.bootstrap.version == 4 ?
40919         this.el : Roo.get(this.el.dom.firstChild, true);
40920
40921     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40922     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40923
40924     
40925 //    this.el.on("mousedown", this.onTabMouseDown, this);
40926     this.el.on("click", this.onTabClick, this);
40927     /** @private */
40928     if(config.closable){
40929         var c = Roo.get(els.close, true);
40930         c.dom.title = this.closeText;
40931         c.addClassOnOver("close-over");
40932         c.on("click", this.closeClick, this);
40933      }
40934
40935     this.addEvents({
40936          /**
40937          * @event activate
40938          * Fires when this tab becomes the active tab.
40939          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40940          * @param {Roo.TabPanelItem} this
40941          */
40942         "activate": true,
40943         /**
40944          * @event beforeclose
40945          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40946          * @param {Roo.TabPanelItem} this
40947          * @param {Object} e Set cancel to true on this object to cancel the close.
40948          */
40949         "beforeclose": true,
40950         /**
40951          * @event close
40952          * Fires when this tab is closed.
40953          * @param {Roo.TabPanelItem} this
40954          */
40955          "close": true,
40956         /**
40957          * @event deactivate
40958          * Fires when this tab is no longer the active tab.
40959          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40960          * @param {Roo.TabPanelItem} this
40961          */
40962          "deactivate" : true
40963     });
40964     this.hidden = false;
40965
40966     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40967 };
40968
40969 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40970            {
40971     purgeListeners : function(){
40972        Roo.util.Observable.prototype.purgeListeners.call(this);
40973        this.el.removeAllListeners();
40974     },
40975     /**
40976      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40977      */
40978     show : function(){
40979         this.status_node.addClass("active");
40980         this.showAction();
40981         if(Roo.isOpera){
40982             this.tabPanel.stripWrap.repaint();
40983         }
40984         this.fireEvent("activate", this.tabPanel, this);
40985     },
40986
40987     /**
40988      * Returns true if this tab is the active tab.
40989      * @return {Boolean}
40990      */
40991     isActive : function(){
40992         return this.tabPanel.getActiveTab() == this;
40993     },
40994
40995     /**
40996      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40997      */
40998     hide : function(){
40999         this.status_node.removeClass("active");
41000         this.hideAction();
41001         this.fireEvent("deactivate", this.tabPanel, this);
41002     },
41003
41004     hideAction : function(){
41005         this.bodyEl.hide();
41006         this.bodyEl.setStyle("position", "absolute");
41007         this.bodyEl.setLeft("-20000px");
41008         this.bodyEl.setTop("-20000px");
41009     },
41010
41011     showAction : function(){
41012         this.bodyEl.setStyle("position", "relative");
41013         this.bodyEl.setTop("");
41014         this.bodyEl.setLeft("");
41015         this.bodyEl.show();
41016     },
41017
41018     /**
41019      * Set the tooltip for the tab.
41020      * @param {String} tooltip The tab's tooltip
41021      */
41022     setTooltip : function(text){
41023         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41024             this.textEl.dom.qtip = text;
41025             this.textEl.dom.removeAttribute('title');
41026         }else{
41027             this.textEl.dom.title = text;
41028         }
41029     },
41030
41031     onTabClick : function(e){
41032         e.preventDefault();
41033         this.tabPanel.activate(this.id);
41034     },
41035
41036     onTabMouseDown : function(e){
41037         e.preventDefault();
41038         this.tabPanel.activate(this.id);
41039     },
41040 /*
41041     getWidth : function(){
41042         return this.inner.getWidth();
41043     },
41044
41045     setWidth : function(width){
41046         var iwidth = width - this.linode.getPadding("lr");
41047         this.inner.setWidth(iwidth);
41048         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41049         this.linode.setWidth(width);
41050     },
41051 */
41052     /**
41053      * Show or hide the tab
41054      * @param {Boolean} hidden True to hide or false to show.
41055      */
41056     setHidden : function(hidden){
41057         this.hidden = hidden;
41058         this.linode.setStyle("display", hidden ? "none" : "");
41059     },
41060
41061     /**
41062      * Returns true if this tab is "hidden"
41063      * @return {Boolean}
41064      */
41065     isHidden : function(){
41066         return this.hidden;
41067     },
41068
41069     /**
41070      * Returns the text for this tab
41071      * @return {String}
41072      */
41073     getText : function(){
41074         return this.text;
41075     },
41076     /*
41077     autoSize : function(){
41078         //this.el.beginMeasure();
41079         this.textEl.setWidth(1);
41080         /*
41081          *  #2804 [new] Tabs in Roojs
41082          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41083          */
41084         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41085         //this.el.endMeasure();
41086     //},
41087
41088     /**
41089      * Sets the text for the tab (Note: this also sets the tooltip text)
41090      * @param {String} text The tab's text and tooltip
41091      */
41092     setText : function(text){
41093         this.text = text;
41094         this.textEl.update(text);
41095         this.setTooltip(text);
41096         //if(!this.tabPanel.resizeTabs){
41097         //    this.autoSize();
41098         //}
41099     },
41100     /**
41101      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41102      */
41103     activate : function(){
41104         this.tabPanel.activate(this.id);
41105     },
41106
41107     /**
41108      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41109      */
41110     disable : function(){
41111         if(this.tabPanel.active != this){
41112             this.disabled = true;
41113             this.status_node.addClass("disabled");
41114         }
41115     },
41116
41117     /**
41118      * Enables this TabPanelItem if it was previously disabled.
41119      */
41120     enable : function(){
41121         this.disabled = false;
41122         this.status_node.removeClass("disabled");
41123     },
41124
41125     /**
41126      * Sets the content for this TabPanelItem.
41127      * @param {String} content The content
41128      * @param {Boolean} loadScripts true to look for and load scripts
41129      */
41130     setContent : function(content, loadScripts){
41131         this.bodyEl.update(content, loadScripts);
41132     },
41133
41134     /**
41135      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41136      * @return {Roo.UpdateManager} The UpdateManager
41137      */
41138     getUpdateManager : function(){
41139         return this.bodyEl.getUpdateManager();
41140     },
41141
41142     /**
41143      * Set a URL to be used to load the content for this TabPanelItem.
41144      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41145      * @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)
41146      * @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)
41147      * @return {Roo.UpdateManager} The UpdateManager
41148      */
41149     setUrl : function(url, params, loadOnce){
41150         if(this.refreshDelegate){
41151             this.un('activate', this.refreshDelegate);
41152         }
41153         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41154         this.on("activate", this.refreshDelegate);
41155         return this.bodyEl.getUpdateManager();
41156     },
41157
41158     /** @private */
41159     _handleRefresh : function(url, params, loadOnce){
41160         if(!loadOnce || !this.loaded){
41161             var updater = this.bodyEl.getUpdateManager();
41162             updater.update(url, params, this._setLoaded.createDelegate(this));
41163         }
41164     },
41165
41166     /**
41167      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41168      *   Will fail silently if the setUrl method has not been called.
41169      *   This does not activate the panel, just updates its content.
41170      */
41171     refresh : function(){
41172         if(this.refreshDelegate){
41173            this.loaded = false;
41174            this.refreshDelegate();
41175         }
41176     },
41177
41178     /** @private */
41179     _setLoaded : function(){
41180         this.loaded = true;
41181     },
41182
41183     /** @private */
41184     closeClick : function(e){
41185         var o = {};
41186         e.stopEvent();
41187         this.fireEvent("beforeclose", this, o);
41188         if(o.cancel !== true){
41189             this.tabPanel.removeTab(this.id);
41190         }
41191     },
41192     /**
41193      * The text displayed in the tooltip for the close icon.
41194      * @type String
41195      */
41196     closeText : "Close this tab"
41197 });
41198 /**
41199 *    This script refer to:
41200 *    Title: International Telephone Input
41201 *    Author: Jack O'Connor
41202 *    Code version:  v12.1.12
41203 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41204 **/
41205
41206 Roo.bootstrap.PhoneInputData = function() {
41207     var d = [
41208       [
41209         "Afghanistan (‫افغانستان‬‎)",
41210         "af",
41211         "93"
41212       ],
41213       [
41214         "Albania (Shqipëri)",
41215         "al",
41216         "355"
41217       ],
41218       [
41219         "Algeria (‫الجزائر‬‎)",
41220         "dz",
41221         "213"
41222       ],
41223       [
41224         "American Samoa",
41225         "as",
41226         "1684"
41227       ],
41228       [
41229         "Andorra",
41230         "ad",
41231         "376"
41232       ],
41233       [
41234         "Angola",
41235         "ao",
41236         "244"
41237       ],
41238       [
41239         "Anguilla",
41240         "ai",
41241         "1264"
41242       ],
41243       [
41244         "Antigua and Barbuda",
41245         "ag",
41246         "1268"
41247       ],
41248       [
41249         "Argentina",
41250         "ar",
41251         "54"
41252       ],
41253       [
41254         "Armenia (Հայաստան)",
41255         "am",
41256         "374"
41257       ],
41258       [
41259         "Aruba",
41260         "aw",
41261         "297"
41262       ],
41263       [
41264         "Australia",
41265         "au",
41266         "61",
41267         0
41268       ],
41269       [
41270         "Austria (Österreich)",
41271         "at",
41272         "43"
41273       ],
41274       [
41275         "Azerbaijan (Azərbaycan)",
41276         "az",
41277         "994"
41278       ],
41279       [
41280         "Bahamas",
41281         "bs",
41282         "1242"
41283       ],
41284       [
41285         "Bahrain (‫البحرين‬‎)",
41286         "bh",
41287         "973"
41288       ],
41289       [
41290         "Bangladesh (বাংলাদেশ)",
41291         "bd",
41292         "880"
41293       ],
41294       [
41295         "Barbados",
41296         "bb",
41297         "1246"
41298       ],
41299       [
41300         "Belarus (Беларусь)",
41301         "by",
41302         "375"
41303       ],
41304       [
41305         "Belgium (België)",
41306         "be",
41307         "32"
41308       ],
41309       [
41310         "Belize",
41311         "bz",
41312         "501"
41313       ],
41314       [
41315         "Benin (Bénin)",
41316         "bj",
41317         "229"
41318       ],
41319       [
41320         "Bermuda",
41321         "bm",
41322         "1441"
41323       ],
41324       [
41325         "Bhutan (འབྲུག)",
41326         "bt",
41327         "975"
41328       ],
41329       [
41330         "Bolivia",
41331         "bo",
41332         "591"
41333       ],
41334       [
41335         "Bosnia and Herzegovina (Босна и Херцеговина)",
41336         "ba",
41337         "387"
41338       ],
41339       [
41340         "Botswana",
41341         "bw",
41342         "267"
41343       ],
41344       [
41345         "Brazil (Brasil)",
41346         "br",
41347         "55"
41348       ],
41349       [
41350         "British Indian Ocean Territory",
41351         "io",
41352         "246"
41353       ],
41354       [
41355         "British Virgin Islands",
41356         "vg",
41357         "1284"
41358       ],
41359       [
41360         "Brunei",
41361         "bn",
41362         "673"
41363       ],
41364       [
41365         "Bulgaria (България)",
41366         "bg",
41367         "359"
41368       ],
41369       [
41370         "Burkina Faso",
41371         "bf",
41372         "226"
41373       ],
41374       [
41375         "Burundi (Uburundi)",
41376         "bi",
41377         "257"
41378       ],
41379       [
41380         "Cambodia (កម្ពុជា)",
41381         "kh",
41382         "855"
41383       ],
41384       [
41385         "Cameroon (Cameroun)",
41386         "cm",
41387         "237"
41388       ],
41389       [
41390         "Canada",
41391         "ca",
41392         "1",
41393         1,
41394         ["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"]
41395       ],
41396       [
41397         "Cape Verde (Kabu Verdi)",
41398         "cv",
41399         "238"
41400       ],
41401       [
41402         "Caribbean Netherlands",
41403         "bq",
41404         "599",
41405         1
41406       ],
41407       [
41408         "Cayman Islands",
41409         "ky",
41410         "1345"
41411       ],
41412       [
41413         "Central African Republic (République centrafricaine)",
41414         "cf",
41415         "236"
41416       ],
41417       [
41418         "Chad (Tchad)",
41419         "td",
41420         "235"
41421       ],
41422       [
41423         "Chile",
41424         "cl",
41425         "56"
41426       ],
41427       [
41428         "China (中国)",
41429         "cn",
41430         "86"
41431       ],
41432       [
41433         "Christmas Island",
41434         "cx",
41435         "61",
41436         2
41437       ],
41438       [
41439         "Cocos (Keeling) Islands",
41440         "cc",
41441         "61",
41442         1
41443       ],
41444       [
41445         "Colombia",
41446         "co",
41447         "57"
41448       ],
41449       [
41450         "Comoros (‫جزر القمر‬‎)",
41451         "km",
41452         "269"
41453       ],
41454       [
41455         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41456         "cd",
41457         "243"
41458       ],
41459       [
41460         "Congo (Republic) (Congo-Brazzaville)",
41461         "cg",
41462         "242"
41463       ],
41464       [
41465         "Cook Islands",
41466         "ck",
41467         "682"
41468       ],
41469       [
41470         "Costa Rica",
41471         "cr",
41472         "506"
41473       ],
41474       [
41475         "Côte d’Ivoire",
41476         "ci",
41477         "225"
41478       ],
41479       [
41480         "Croatia (Hrvatska)",
41481         "hr",
41482         "385"
41483       ],
41484       [
41485         "Cuba",
41486         "cu",
41487         "53"
41488       ],
41489       [
41490         "Curaçao",
41491         "cw",
41492         "599",
41493         0
41494       ],
41495       [
41496         "Cyprus (Κύπρος)",
41497         "cy",
41498         "357"
41499       ],
41500       [
41501         "Czech Republic (Česká republika)",
41502         "cz",
41503         "420"
41504       ],
41505       [
41506         "Denmark (Danmark)",
41507         "dk",
41508         "45"
41509       ],
41510       [
41511         "Djibouti",
41512         "dj",
41513         "253"
41514       ],
41515       [
41516         "Dominica",
41517         "dm",
41518         "1767"
41519       ],
41520       [
41521         "Dominican Republic (República Dominicana)",
41522         "do",
41523         "1",
41524         2,
41525         ["809", "829", "849"]
41526       ],
41527       [
41528         "Ecuador",
41529         "ec",
41530         "593"
41531       ],
41532       [
41533         "Egypt (‫مصر‬‎)",
41534         "eg",
41535         "20"
41536       ],
41537       [
41538         "El Salvador",
41539         "sv",
41540         "503"
41541       ],
41542       [
41543         "Equatorial Guinea (Guinea Ecuatorial)",
41544         "gq",
41545         "240"
41546       ],
41547       [
41548         "Eritrea",
41549         "er",
41550         "291"
41551       ],
41552       [
41553         "Estonia (Eesti)",
41554         "ee",
41555         "372"
41556       ],
41557       [
41558         "Ethiopia",
41559         "et",
41560         "251"
41561       ],
41562       [
41563         "Falkland Islands (Islas Malvinas)",
41564         "fk",
41565         "500"
41566       ],
41567       [
41568         "Faroe Islands (Føroyar)",
41569         "fo",
41570         "298"
41571       ],
41572       [
41573         "Fiji",
41574         "fj",
41575         "679"
41576       ],
41577       [
41578         "Finland (Suomi)",
41579         "fi",
41580         "358",
41581         0
41582       ],
41583       [
41584         "France",
41585         "fr",
41586         "33"
41587       ],
41588       [
41589         "French Guiana (Guyane française)",
41590         "gf",
41591         "594"
41592       ],
41593       [
41594         "French Polynesia (Polynésie française)",
41595         "pf",
41596         "689"
41597       ],
41598       [
41599         "Gabon",
41600         "ga",
41601         "241"
41602       ],
41603       [
41604         "Gambia",
41605         "gm",
41606         "220"
41607       ],
41608       [
41609         "Georgia (საქართველო)",
41610         "ge",
41611         "995"
41612       ],
41613       [
41614         "Germany (Deutschland)",
41615         "de",
41616         "49"
41617       ],
41618       [
41619         "Ghana (Gaana)",
41620         "gh",
41621         "233"
41622       ],
41623       [
41624         "Gibraltar",
41625         "gi",
41626         "350"
41627       ],
41628       [
41629         "Greece (Ελλάδα)",
41630         "gr",
41631         "30"
41632       ],
41633       [
41634         "Greenland (Kalaallit Nunaat)",
41635         "gl",
41636         "299"
41637       ],
41638       [
41639         "Grenada",
41640         "gd",
41641         "1473"
41642       ],
41643       [
41644         "Guadeloupe",
41645         "gp",
41646         "590",
41647         0
41648       ],
41649       [
41650         "Guam",
41651         "gu",
41652         "1671"
41653       ],
41654       [
41655         "Guatemala",
41656         "gt",
41657         "502"
41658       ],
41659       [
41660         "Guernsey",
41661         "gg",
41662         "44",
41663         1
41664       ],
41665       [
41666         "Guinea (Guinée)",
41667         "gn",
41668         "224"
41669       ],
41670       [
41671         "Guinea-Bissau (Guiné Bissau)",
41672         "gw",
41673         "245"
41674       ],
41675       [
41676         "Guyana",
41677         "gy",
41678         "592"
41679       ],
41680       [
41681         "Haiti",
41682         "ht",
41683         "509"
41684       ],
41685       [
41686         "Honduras",
41687         "hn",
41688         "504"
41689       ],
41690       [
41691         "Hong Kong (香港)",
41692         "hk",
41693         "852"
41694       ],
41695       [
41696         "Hungary (Magyarország)",
41697         "hu",
41698         "36"
41699       ],
41700       [
41701         "Iceland (Ísland)",
41702         "is",
41703         "354"
41704       ],
41705       [
41706         "India (भारत)",
41707         "in",
41708         "91"
41709       ],
41710       [
41711         "Indonesia",
41712         "id",
41713         "62"
41714       ],
41715       [
41716         "Iran (‫ایران‬‎)",
41717         "ir",
41718         "98"
41719       ],
41720       [
41721         "Iraq (‫العراق‬‎)",
41722         "iq",
41723         "964"
41724       ],
41725       [
41726         "Ireland",
41727         "ie",
41728         "353"
41729       ],
41730       [
41731         "Isle of Man",
41732         "im",
41733         "44",
41734         2
41735       ],
41736       [
41737         "Israel (‫ישראל‬‎)",
41738         "il",
41739         "972"
41740       ],
41741       [
41742         "Italy (Italia)",
41743         "it",
41744         "39",
41745         0
41746       ],
41747       [
41748         "Jamaica",
41749         "jm",
41750         "1876"
41751       ],
41752       [
41753         "Japan (日本)",
41754         "jp",
41755         "81"
41756       ],
41757       [
41758         "Jersey",
41759         "je",
41760         "44",
41761         3
41762       ],
41763       [
41764         "Jordan (‫الأردن‬‎)",
41765         "jo",
41766         "962"
41767       ],
41768       [
41769         "Kazakhstan (Казахстан)",
41770         "kz",
41771         "7",
41772         1
41773       ],
41774       [
41775         "Kenya",
41776         "ke",
41777         "254"
41778       ],
41779       [
41780         "Kiribati",
41781         "ki",
41782         "686"
41783       ],
41784       [
41785         "Kosovo",
41786         "xk",
41787         "383"
41788       ],
41789       [
41790         "Kuwait (‫الكويت‬‎)",
41791         "kw",
41792         "965"
41793       ],
41794       [
41795         "Kyrgyzstan (Кыргызстан)",
41796         "kg",
41797         "996"
41798       ],
41799       [
41800         "Laos (ລາວ)",
41801         "la",
41802         "856"
41803       ],
41804       [
41805         "Latvia (Latvija)",
41806         "lv",
41807         "371"
41808       ],
41809       [
41810         "Lebanon (‫لبنان‬‎)",
41811         "lb",
41812         "961"
41813       ],
41814       [
41815         "Lesotho",
41816         "ls",
41817         "266"
41818       ],
41819       [
41820         "Liberia",
41821         "lr",
41822         "231"
41823       ],
41824       [
41825         "Libya (‫ليبيا‬‎)",
41826         "ly",
41827         "218"
41828       ],
41829       [
41830         "Liechtenstein",
41831         "li",
41832         "423"
41833       ],
41834       [
41835         "Lithuania (Lietuva)",
41836         "lt",
41837         "370"
41838       ],
41839       [
41840         "Luxembourg",
41841         "lu",
41842         "352"
41843       ],
41844       [
41845         "Macau (澳門)",
41846         "mo",
41847         "853"
41848       ],
41849       [
41850         "Macedonia (FYROM) (Македонија)",
41851         "mk",
41852         "389"
41853       ],
41854       [
41855         "Madagascar (Madagasikara)",
41856         "mg",
41857         "261"
41858       ],
41859       [
41860         "Malawi",
41861         "mw",
41862         "265"
41863       ],
41864       [
41865         "Malaysia",
41866         "my",
41867         "60"
41868       ],
41869       [
41870         "Maldives",
41871         "mv",
41872         "960"
41873       ],
41874       [
41875         "Mali",
41876         "ml",
41877         "223"
41878       ],
41879       [
41880         "Malta",
41881         "mt",
41882         "356"
41883       ],
41884       [
41885         "Marshall Islands",
41886         "mh",
41887         "692"
41888       ],
41889       [
41890         "Martinique",
41891         "mq",
41892         "596"
41893       ],
41894       [
41895         "Mauritania (‫موريتانيا‬‎)",
41896         "mr",
41897         "222"
41898       ],
41899       [
41900         "Mauritius (Moris)",
41901         "mu",
41902         "230"
41903       ],
41904       [
41905         "Mayotte",
41906         "yt",
41907         "262",
41908         1
41909       ],
41910       [
41911         "Mexico (México)",
41912         "mx",
41913         "52"
41914       ],
41915       [
41916         "Micronesia",
41917         "fm",
41918         "691"
41919       ],
41920       [
41921         "Moldova (Republica Moldova)",
41922         "md",
41923         "373"
41924       ],
41925       [
41926         "Monaco",
41927         "mc",
41928         "377"
41929       ],
41930       [
41931         "Mongolia (Монгол)",
41932         "mn",
41933         "976"
41934       ],
41935       [
41936         "Montenegro (Crna Gora)",
41937         "me",
41938         "382"
41939       ],
41940       [
41941         "Montserrat",
41942         "ms",
41943         "1664"
41944       ],
41945       [
41946         "Morocco (‫المغرب‬‎)",
41947         "ma",
41948         "212",
41949         0
41950       ],
41951       [
41952         "Mozambique (Moçambique)",
41953         "mz",
41954         "258"
41955       ],
41956       [
41957         "Myanmar (Burma) (မြန်မာ)",
41958         "mm",
41959         "95"
41960       ],
41961       [
41962         "Namibia (Namibië)",
41963         "na",
41964         "264"
41965       ],
41966       [
41967         "Nauru",
41968         "nr",
41969         "674"
41970       ],
41971       [
41972         "Nepal (नेपाल)",
41973         "np",
41974         "977"
41975       ],
41976       [
41977         "Netherlands (Nederland)",
41978         "nl",
41979         "31"
41980       ],
41981       [
41982         "New Caledonia (Nouvelle-Calédonie)",
41983         "nc",
41984         "687"
41985       ],
41986       [
41987         "New Zealand",
41988         "nz",
41989         "64"
41990       ],
41991       [
41992         "Nicaragua",
41993         "ni",
41994         "505"
41995       ],
41996       [
41997         "Niger (Nijar)",
41998         "ne",
41999         "227"
42000       ],
42001       [
42002         "Nigeria",
42003         "ng",
42004         "234"
42005       ],
42006       [
42007         "Niue",
42008         "nu",
42009         "683"
42010       ],
42011       [
42012         "Norfolk Island",
42013         "nf",
42014         "672"
42015       ],
42016       [
42017         "North Korea (조선 민주주의 인민 공화국)",
42018         "kp",
42019         "850"
42020       ],
42021       [
42022         "Northern Mariana Islands",
42023         "mp",
42024         "1670"
42025       ],
42026       [
42027         "Norway (Norge)",
42028         "no",
42029         "47",
42030         0
42031       ],
42032       [
42033         "Oman (‫عُمان‬‎)",
42034         "om",
42035         "968"
42036       ],
42037       [
42038         "Pakistan (‫پاکستان‬‎)",
42039         "pk",
42040         "92"
42041       ],
42042       [
42043         "Palau",
42044         "pw",
42045         "680"
42046       ],
42047       [
42048         "Palestine (‫فلسطين‬‎)",
42049         "ps",
42050         "970"
42051       ],
42052       [
42053         "Panama (Panamá)",
42054         "pa",
42055         "507"
42056       ],
42057       [
42058         "Papua New Guinea",
42059         "pg",
42060         "675"
42061       ],
42062       [
42063         "Paraguay",
42064         "py",
42065         "595"
42066       ],
42067       [
42068         "Peru (Perú)",
42069         "pe",
42070         "51"
42071       ],
42072       [
42073         "Philippines",
42074         "ph",
42075         "63"
42076       ],
42077       [
42078         "Poland (Polska)",
42079         "pl",
42080         "48"
42081       ],
42082       [
42083         "Portugal",
42084         "pt",
42085         "351"
42086       ],
42087       [
42088         "Puerto Rico",
42089         "pr",
42090         "1",
42091         3,
42092         ["787", "939"]
42093       ],
42094       [
42095         "Qatar (‫قطر‬‎)",
42096         "qa",
42097         "974"
42098       ],
42099       [
42100         "Réunion (La Réunion)",
42101         "re",
42102         "262",
42103         0
42104       ],
42105       [
42106         "Romania (România)",
42107         "ro",
42108         "40"
42109       ],
42110       [
42111         "Russia (Россия)",
42112         "ru",
42113         "7",
42114         0
42115       ],
42116       [
42117         "Rwanda",
42118         "rw",
42119         "250"
42120       ],
42121       [
42122         "Saint Barthélemy",
42123         "bl",
42124         "590",
42125         1
42126       ],
42127       [
42128         "Saint Helena",
42129         "sh",
42130         "290"
42131       ],
42132       [
42133         "Saint Kitts and Nevis",
42134         "kn",
42135         "1869"
42136       ],
42137       [
42138         "Saint Lucia",
42139         "lc",
42140         "1758"
42141       ],
42142       [
42143         "Saint Martin (Saint-Martin (partie française))",
42144         "mf",
42145         "590",
42146         2
42147       ],
42148       [
42149         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42150         "pm",
42151         "508"
42152       ],
42153       [
42154         "Saint Vincent and the Grenadines",
42155         "vc",
42156         "1784"
42157       ],
42158       [
42159         "Samoa",
42160         "ws",
42161         "685"
42162       ],
42163       [
42164         "San Marino",
42165         "sm",
42166         "378"
42167       ],
42168       [
42169         "São Tomé and Príncipe (São Tomé e Príncipe)",
42170         "st",
42171         "239"
42172       ],
42173       [
42174         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42175         "sa",
42176         "966"
42177       ],
42178       [
42179         "Senegal (Sénégal)",
42180         "sn",
42181         "221"
42182       ],
42183       [
42184         "Serbia (Србија)",
42185         "rs",
42186         "381"
42187       ],
42188       [
42189         "Seychelles",
42190         "sc",
42191         "248"
42192       ],
42193       [
42194         "Sierra Leone",
42195         "sl",
42196         "232"
42197       ],
42198       [
42199         "Singapore",
42200         "sg",
42201         "65"
42202       ],
42203       [
42204         "Sint Maarten",
42205         "sx",
42206         "1721"
42207       ],
42208       [
42209         "Slovakia (Slovensko)",
42210         "sk",
42211         "421"
42212       ],
42213       [
42214         "Slovenia (Slovenija)",
42215         "si",
42216         "386"
42217       ],
42218       [
42219         "Solomon Islands",
42220         "sb",
42221         "677"
42222       ],
42223       [
42224         "Somalia (Soomaaliya)",
42225         "so",
42226         "252"
42227       ],
42228       [
42229         "South Africa",
42230         "za",
42231         "27"
42232       ],
42233       [
42234         "South Korea (대한민국)",
42235         "kr",
42236         "82"
42237       ],
42238       [
42239         "South Sudan (‫جنوب السودان‬‎)",
42240         "ss",
42241         "211"
42242       ],
42243       [
42244         "Spain (España)",
42245         "es",
42246         "34"
42247       ],
42248       [
42249         "Sri Lanka (ශ්‍රී ලංකාව)",
42250         "lk",
42251         "94"
42252       ],
42253       [
42254         "Sudan (‫السودان‬‎)",
42255         "sd",
42256         "249"
42257       ],
42258       [
42259         "Suriname",
42260         "sr",
42261         "597"
42262       ],
42263       [
42264         "Svalbard and Jan Mayen",
42265         "sj",
42266         "47",
42267         1
42268       ],
42269       [
42270         "Swaziland",
42271         "sz",
42272         "268"
42273       ],
42274       [
42275         "Sweden (Sverige)",
42276         "se",
42277         "46"
42278       ],
42279       [
42280         "Switzerland (Schweiz)",
42281         "ch",
42282         "41"
42283       ],
42284       [
42285         "Syria (‫سوريا‬‎)",
42286         "sy",
42287         "963"
42288       ],
42289       [
42290         "Taiwan (台灣)",
42291         "tw",
42292         "886"
42293       ],
42294       [
42295         "Tajikistan",
42296         "tj",
42297         "992"
42298       ],
42299       [
42300         "Tanzania",
42301         "tz",
42302         "255"
42303       ],
42304       [
42305         "Thailand (ไทย)",
42306         "th",
42307         "66"
42308       ],
42309       [
42310         "Timor-Leste",
42311         "tl",
42312         "670"
42313       ],
42314       [
42315         "Togo",
42316         "tg",
42317         "228"
42318       ],
42319       [
42320         "Tokelau",
42321         "tk",
42322         "690"
42323       ],
42324       [
42325         "Tonga",
42326         "to",
42327         "676"
42328       ],
42329       [
42330         "Trinidad and Tobago",
42331         "tt",
42332         "1868"
42333       ],
42334       [
42335         "Tunisia (‫تونس‬‎)",
42336         "tn",
42337         "216"
42338       ],
42339       [
42340         "Turkey (Türkiye)",
42341         "tr",
42342         "90"
42343       ],
42344       [
42345         "Turkmenistan",
42346         "tm",
42347         "993"
42348       ],
42349       [
42350         "Turks and Caicos Islands",
42351         "tc",
42352         "1649"
42353       ],
42354       [
42355         "Tuvalu",
42356         "tv",
42357         "688"
42358       ],
42359       [
42360         "U.S. Virgin Islands",
42361         "vi",
42362         "1340"
42363       ],
42364       [
42365         "Uganda",
42366         "ug",
42367         "256"
42368       ],
42369       [
42370         "Ukraine (Україна)",
42371         "ua",
42372         "380"
42373       ],
42374       [
42375         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42376         "ae",
42377         "971"
42378       ],
42379       [
42380         "United Kingdom",
42381         "gb",
42382         "44",
42383         0
42384       ],
42385       [
42386         "United States",
42387         "us",
42388         "1",
42389         0
42390       ],
42391       [
42392         "Uruguay",
42393         "uy",
42394         "598"
42395       ],
42396       [
42397         "Uzbekistan (Oʻzbekiston)",
42398         "uz",
42399         "998"
42400       ],
42401       [
42402         "Vanuatu",
42403         "vu",
42404         "678"
42405       ],
42406       [
42407         "Vatican City (Città del Vaticano)",
42408         "va",
42409         "39",
42410         1
42411       ],
42412       [
42413         "Venezuela",
42414         "ve",
42415         "58"
42416       ],
42417       [
42418         "Vietnam (Việt Nam)",
42419         "vn",
42420         "84"
42421       ],
42422       [
42423         "Wallis and Futuna (Wallis-et-Futuna)",
42424         "wf",
42425         "681"
42426       ],
42427       [
42428         "Western Sahara (‫الصحراء الغربية‬‎)",
42429         "eh",
42430         "212",
42431         1
42432       ],
42433       [
42434         "Yemen (‫اليمن‬‎)",
42435         "ye",
42436         "967"
42437       ],
42438       [
42439         "Zambia",
42440         "zm",
42441         "260"
42442       ],
42443       [
42444         "Zimbabwe",
42445         "zw",
42446         "263"
42447       ],
42448       [
42449         "Åland Islands",
42450         "ax",
42451         "358",
42452         1
42453       ]
42454   ];
42455   
42456   return d;
42457 }/**
42458 *    This script refer to:
42459 *    Title: International Telephone Input
42460 *    Author: Jack O'Connor
42461 *    Code version:  v12.1.12
42462 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42463 **/
42464
42465 /**
42466  * @class Roo.bootstrap.PhoneInput
42467  * @extends Roo.bootstrap.TriggerField
42468  * An input with International dial-code selection
42469  
42470  * @cfg {String} defaultDialCode default '+852'
42471  * @cfg {Array} preferedCountries default []
42472   
42473  * @constructor
42474  * Create a new PhoneInput.
42475  * @param {Object} config Configuration options
42476  */
42477
42478 Roo.bootstrap.PhoneInput = function(config) {
42479     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42480 };
42481
42482 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42483         
42484         listWidth: undefined,
42485         
42486         selectedClass: 'active',
42487         
42488         invalidClass : "has-warning",
42489         
42490         validClass: 'has-success',
42491         
42492         allowed: '0123456789',
42493         
42494         max_length: 15,
42495         
42496         /**
42497          * @cfg {String} defaultDialCode The default dial code when initializing the input
42498          */
42499         defaultDialCode: '+852',
42500         
42501         /**
42502          * @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
42503          */
42504         preferedCountries: false,
42505         
42506         getAutoCreate : function()
42507         {
42508             var data = Roo.bootstrap.PhoneInputData();
42509             var align = this.labelAlign || this.parentLabelAlign();
42510             var id = Roo.id();
42511             
42512             this.allCountries = [];
42513             this.dialCodeMapping = [];
42514             
42515             for (var i = 0; i < data.length; i++) {
42516               var c = data[i];
42517               this.allCountries[i] = {
42518                 name: c[0],
42519                 iso2: c[1],
42520                 dialCode: c[2],
42521                 priority: c[3] || 0,
42522                 areaCodes: c[4] || null
42523               };
42524               this.dialCodeMapping[c[2]] = {
42525                   name: c[0],
42526                   iso2: c[1],
42527                   priority: c[3] || 0,
42528                   areaCodes: c[4] || null
42529               };
42530             }
42531             
42532             var cfg = {
42533                 cls: 'form-group',
42534                 cn: []
42535             };
42536             
42537             var input =  {
42538                 tag: 'input',
42539                 id : id,
42540                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42541                 maxlength: this.max_length,
42542                 cls : 'form-control tel-input',
42543                 autocomplete: 'new-password'
42544             };
42545             
42546             var hiddenInput = {
42547                 tag: 'input',
42548                 type: 'hidden',
42549                 cls: 'hidden-tel-input'
42550             };
42551             
42552             if (this.name) {
42553                 hiddenInput.name = this.name;
42554             }
42555             
42556             if (this.disabled) {
42557                 input.disabled = true;
42558             }
42559             
42560             var flag_container = {
42561                 tag: 'div',
42562                 cls: 'flag-box',
42563                 cn: [
42564                     {
42565                         tag: 'div',
42566                         cls: 'flag'
42567                     },
42568                     {
42569                         tag: 'div',
42570                         cls: 'caret'
42571                     }
42572                 ]
42573             };
42574             
42575             var box = {
42576                 tag: 'div',
42577                 cls: this.hasFeedback ? 'has-feedback' : '',
42578                 cn: [
42579                     hiddenInput,
42580                     input,
42581                     {
42582                         tag: 'input',
42583                         cls: 'dial-code-holder',
42584                         disabled: true
42585                     }
42586                 ]
42587             };
42588             
42589             var container = {
42590                 cls: 'roo-select2-container input-group',
42591                 cn: [
42592                     flag_container,
42593                     box
42594                 ]
42595             };
42596             
42597             if (this.fieldLabel.length) {
42598                 var indicator = {
42599                     tag: 'i',
42600                     tooltip: 'This field is required'
42601                 };
42602                 
42603                 var label = {
42604                     tag: 'label',
42605                     'for':  id,
42606                     cls: 'control-label',
42607                     cn: []
42608                 };
42609                 
42610                 var label_text = {
42611                     tag: 'span',
42612                     html: this.fieldLabel
42613                 };
42614                 
42615                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42616                 label.cn = [
42617                     indicator,
42618                     label_text
42619                 ];
42620                 
42621                 if(this.indicatorpos == 'right') {
42622                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42623                     label.cn = [
42624                         label_text,
42625                         indicator
42626                     ];
42627                 }
42628                 
42629                 if(align == 'left') {
42630                     container = {
42631                         tag: 'div',
42632                         cn: [
42633                             container
42634                         ]
42635                     };
42636                     
42637                     if(this.labelWidth > 12){
42638                         label.style = "width: " + this.labelWidth + 'px';
42639                     }
42640                     if(this.labelWidth < 13 && this.labelmd == 0){
42641                         this.labelmd = this.labelWidth;
42642                     }
42643                     if(this.labellg > 0){
42644                         label.cls += ' col-lg-' + this.labellg;
42645                         input.cls += ' col-lg-' + (12 - this.labellg);
42646                     }
42647                     if(this.labelmd > 0){
42648                         label.cls += ' col-md-' + this.labelmd;
42649                         container.cls += ' col-md-' + (12 - this.labelmd);
42650                     }
42651                     if(this.labelsm > 0){
42652                         label.cls += ' col-sm-' + this.labelsm;
42653                         container.cls += ' col-sm-' + (12 - this.labelsm);
42654                     }
42655                     if(this.labelxs > 0){
42656                         label.cls += ' col-xs-' + this.labelxs;
42657                         container.cls += ' col-xs-' + (12 - this.labelxs);
42658                     }
42659                 }
42660             }
42661             
42662             cfg.cn = [
42663                 label,
42664                 container
42665             ];
42666             
42667             var settings = this;
42668             
42669             ['xs','sm','md','lg'].map(function(size){
42670                 if (settings[size]) {
42671                     cfg.cls += ' col-' + size + '-' + settings[size];
42672                 }
42673             });
42674             
42675             this.store = new Roo.data.Store({
42676                 proxy : new Roo.data.MemoryProxy({}),
42677                 reader : new Roo.data.JsonReader({
42678                     fields : [
42679                         {
42680                             'name' : 'name',
42681                             'type' : 'string'
42682                         },
42683                         {
42684                             'name' : 'iso2',
42685                             'type' : 'string'
42686                         },
42687                         {
42688                             'name' : 'dialCode',
42689                             'type' : 'string'
42690                         },
42691                         {
42692                             'name' : 'priority',
42693                             'type' : 'string'
42694                         },
42695                         {
42696                             'name' : 'areaCodes',
42697                             'type' : 'string'
42698                         }
42699                     ]
42700                 })
42701             });
42702             
42703             if(!this.preferedCountries) {
42704                 this.preferedCountries = [
42705                     'hk',
42706                     'gb',
42707                     'us'
42708                 ];
42709             }
42710             
42711             var p = this.preferedCountries.reverse();
42712             
42713             if(p) {
42714                 for (var i = 0; i < p.length; i++) {
42715                     for (var j = 0; j < this.allCountries.length; j++) {
42716                         if(this.allCountries[j].iso2 == p[i]) {
42717                             var t = this.allCountries[j];
42718                             this.allCountries.splice(j,1);
42719                             this.allCountries.unshift(t);
42720                         }
42721                     } 
42722                 }
42723             }
42724             
42725             this.store.proxy.data = {
42726                 success: true,
42727                 data: this.allCountries
42728             };
42729             
42730             return cfg;
42731         },
42732         
42733         initEvents : function()
42734         {
42735             this.createList();
42736             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42737             
42738             this.indicator = this.indicatorEl();
42739             this.flag = this.flagEl();
42740             this.dialCodeHolder = this.dialCodeHolderEl();
42741             
42742             this.trigger = this.el.select('div.flag-box',true).first();
42743             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42744             
42745             var _this = this;
42746             
42747             (function(){
42748                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42749                 _this.list.setWidth(lw);
42750             }).defer(100);
42751             
42752             this.list.on('mouseover', this.onViewOver, this);
42753             this.list.on('mousemove', this.onViewMove, this);
42754             this.inputEl().on("keyup", this.onKeyUp, this);
42755             this.inputEl().on("keypress", this.onKeyPress, this);
42756             
42757             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42758
42759             this.view = new Roo.View(this.list, this.tpl, {
42760                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42761             });
42762             
42763             this.view.on('click', this.onViewClick, this);
42764             this.setValue(this.defaultDialCode);
42765         },
42766         
42767         onTriggerClick : function(e)
42768         {
42769             Roo.log('trigger click');
42770             if(this.disabled){
42771                 return;
42772             }
42773             
42774             if(this.isExpanded()){
42775                 this.collapse();
42776                 this.hasFocus = false;
42777             }else {
42778                 this.store.load({});
42779                 this.hasFocus = true;
42780                 this.expand();
42781             }
42782         },
42783         
42784         isExpanded : function()
42785         {
42786             return this.list.isVisible();
42787         },
42788         
42789         collapse : function()
42790         {
42791             if(!this.isExpanded()){
42792                 return;
42793             }
42794             this.list.hide();
42795             Roo.get(document).un('mousedown', this.collapseIf, this);
42796             Roo.get(document).un('mousewheel', this.collapseIf, this);
42797             this.fireEvent('collapse', this);
42798             this.validate();
42799         },
42800         
42801         expand : function()
42802         {
42803             Roo.log('expand');
42804
42805             if(this.isExpanded() || !this.hasFocus){
42806                 return;
42807             }
42808             
42809             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42810             this.list.setWidth(lw);
42811             
42812             this.list.show();
42813             this.restrictHeight();
42814             
42815             Roo.get(document).on('mousedown', this.collapseIf, this);
42816             Roo.get(document).on('mousewheel', this.collapseIf, this);
42817             
42818             this.fireEvent('expand', this);
42819         },
42820         
42821         restrictHeight : function()
42822         {
42823             this.list.alignTo(this.inputEl(), this.listAlign);
42824             this.list.alignTo(this.inputEl(), this.listAlign);
42825         },
42826         
42827         onViewOver : function(e, t)
42828         {
42829             if(this.inKeyMode){
42830                 return;
42831             }
42832             var item = this.view.findItemFromChild(t);
42833             
42834             if(item){
42835                 var index = this.view.indexOf(item);
42836                 this.select(index, false);
42837             }
42838         },
42839
42840         // private
42841         onViewClick : function(view, doFocus, el, e)
42842         {
42843             var index = this.view.getSelectedIndexes()[0];
42844             
42845             var r = this.store.getAt(index);
42846             
42847             if(r){
42848                 this.onSelect(r, index);
42849             }
42850             if(doFocus !== false && !this.blockFocus){
42851                 this.inputEl().focus();
42852             }
42853         },
42854         
42855         onViewMove : function(e, t)
42856         {
42857             this.inKeyMode = false;
42858         },
42859         
42860         select : function(index, scrollIntoView)
42861         {
42862             this.selectedIndex = index;
42863             this.view.select(index);
42864             if(scrollIntoView !== false){
42865                 var el = this.view.getNode(index);
42866                 if(el){
42867                     this.list.scrollChildIntoView(el, false);
42868                 }
42869             }
42870         },
42871         
42872         createList : function()
42873         {
42874             this.list = Roo.get(document.body).createChild({
42875                 tag: 'ul',
42876                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42877                 style: 'display:none'
42878             });
42879             
42880             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42881         },
42882         
42883         collapseIf : function(e)
42884         {
42885             var in_combo  = e.within(this.el);
42886             var in_list =  e.within(this.list);
42887             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42888             
42889             if (in_combo || in_list || is_list) {
42890                 return;
42891             }
42892             this.collapse();
42893         },
42894         
42895         onSelect : function(record, index)
42896         {
42897             if(this.fireEvent('beforeselect', this, record, index) !== false){
42898                 
42899                 this.setFlagClass(record.data.iso2);
42900                 this.setDialCode(record.data.dialCode);
42901                 this.hasFocus = false;
42902                 this.collapse();
42903                 this.fireEvent('select', this, record, index);
42904             }
42905         },
42906         
42907         flagEl : function()
42908         {
42909             var flag = this.el.select('div.flag',true).first();
42910             if(!flag){
42911                 return false;
42912             }
42913             return flag;
42914         },
42915         
42916         dialCodeHolderEl : function()
42917         {
42918             var d = this.el.select('input.dial-code-holder',true).first();
42919             if(!d){
42920                 return false;
42921             }
42922             return d;
42923         },
42924         
42925         setDialCode : function(v)
42926         {
42927             this.dialCodeHolder.dom.value = '+'+v;
42928         },
42929         
42930         setFlagClass : function(n)
42931         {
42932             this.flag.dom.className = 'flag '+n;
42933         },
42934         
42935         getValue : function()
42936         {
42937             var v = this.inputEl().getValue();
42938             if(this.dialCodeHolder) {
42939                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42940             }
42941             return v;
42942         },
42943         
42944         setValue : function(v)
42945         {
42946             var d = this.getDialCode(v);
42947             
42948             //invalid dial code
42949             if(v.length == 0 || !d || d.length == 0) {
42950                 if(this.rendered){
42951                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42952                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42953                 }
42954                 return;
42955             }
42956             
42957             //valid dial code
42958             this.setFlagClass(this.dialCodeMapping[d].iso2);
42959             this.setDialCode(d);
42960             this.inputEl().dom.value = v.replace('+'+d,'');
42961             this.hiddenEl().dom.value = this.getValue();
42962             
42963             this.validate();
42964         },
42965         
42966         getDialCode : function(v)
42967         {
42968             v = v ||  '';
42969             
42970             if (v.length == 0) {
42971                 return this.dialCodeHolder.dom.value;
42972             }
42973             
42974             var dialCode = "";
42975             if (v.charAt(0) != "+") {
42976                 return false;
42977             }
42978             var numericChars = "";
42979             for (var i = 1; i < v.length; i++) {
42980               var c = v.charAt(i);
42981               if (!isNaN(c)) {
42982                 numericChars += c;
42983                 if (this.dialCodeMapping[numericChars]) {
42984                   dialCode = v.substr(1, i);
42985                 }
42986                 if (numericChars.length == 4) {
42987                   break;
42988                 }
42989               }
42990             }
42991             return dialCode;
42992         },
42993         
42994         reset : function()
42995         {
42996             this.setValue(this.defaultDialCode);
42997             this.validate();
42998         },
42999         
43000         hiddenEl : function()
43001         {
43002             return this.el.select('input.hidden-tel-input',true).first();
43003         },
43004         
43005         // after setting val
43006         onKeyUp : function(e){
43007             this.setValue(this.getValue());
43008         },
43009         
43010         onKeyPress : function(e){
43011             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43012                 e.stopEvent();
43013             }
43014         }
43015         
43016 });
43017 /**
43018  * @class Roo.bootstrap.MoneyField
43019  * @extends Roo.bootstrap.ComboBox
43020  * Bootstrap MoneyField class
43021  * 
43022  * @constructor
43023  * Create a new MoneyField.
43024  * @param {Object} config Configuration options
43025  */
43026
43027 Roo.bootstrap.MoneyField = function(config) {
43028     
43029     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43030     
43031 };
43032
43033 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43034     
43035     /**
43036      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43037      */
43038     allowDecimals : true,
43039     /**
43040      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43041      */
43042     decimalSeparator : ".",
43043     /**
43044      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43045      */
43046     decimalPrecision : 0,
43047     /**
43048      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43049      */
43050     allowNegative : true,
43051     /**
43052      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43053      */
43054     allowZero: true,
43055     /**
43056      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43057      */
43058     minValue : Number.NEGATIVE_INFINITY,
43059     /**
43060      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43061      */
43062     maxValue : Number.MAX_VALUE,
43063     /**
43064      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43065      */
43066     minText : "The minimum value for this field is {0}",
43067     /**
43068      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43069      */
43070     maxText : "The maximum value for this field is {0}",
43071     /**
43072      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43073      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43074      */
43075     nanText : "{0} is not a valid number",
43076     /**
43077      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43078      */
43079     castInt : true,
43080     /**
43081      * @cfg {String} defaults currency of the MoneyField
43082      * value should be in lkey
43083      */
43084     defaultCurrency : false,
43085     /**
43086      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43087      */
43088     thousandsDelimiter : false,
43089     /**
43090      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43091      */
43092     max_length: false,
43093     
43094     inputlg : 9,
43095     inputmd : 9,
43096     inputsm : 9,
43097     inputxs : 6,
43098     
43099     store : false,
43100     
43101     getAutoCreate : function()
43102     {
43103         var align = this.labelAlign || this.parentLabelAlign();
43104         
43105         var id = Roo.id();
43106
43107         var cfg = {
43108             cls: 'form-group',
43109             cn: []
43110         };
43111
43112         var input =  {
43113             tag: 'input',
43114             id : id,
43115             cls : 'form-control roo-money-amount-input',
43116             autocomplete: 'new-password'
43117         };
43118         
43119         var hiddenInput = {
43120             tag: 'input',
43121             type: 'hidden',
43122             id: Roo.id(),
43123             cls: 'hidden-number-input'
43124         };
43125         
43126         if(this.max_length) {
43127             input.maxlength = this.max_length; 
43128         }
43129         
43130         if (this.name) {
43131             hiddenInput.name = this.name;
43132         }
43133
43134         if (this.disabled) {
43135             input.disabled = true;
43136         }
43137
43138         var clg = 12 - this.inputlg;
43139         var cmd = 12 - this.inputmd;
43140         var csm = 12 - this.inputsm;
43141         var cxs = 12 - this.inputxs;
43142         
43143         var container = {
43144             tag : 'div',
43145             cls : 'row roo-money-field',
43146             cn : [
43147                 {
43148                     tag : 'div',
43149                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43150                     cn : [
43151                         {
43152                             tag : 'div',
43153                             cls: 'roo-select2-container input-group',
43154                             cn: [
43155                                 {
43156                                     tag : 'input',
43157                                     cls : 'form-control roo-money-currency-input',
43158                                     autocomplete: 'new-password',
43159                                     readOnly : 1,
43160                                     name : this.currencyName
43161                                 },
43162                                 {
43163                                     tag :'span',
43164                                     cls : 'input-group-addon',
43165                                     cn : [
43166                                         {
43167                                             tag: 'span',
43168                                             cls: 'caret'
43169                                         }
43170                                     ]
43171                                 }
43172                             ]
43173                         }
43174                     ]
43175                 },
43176                 {
43177                     tag : 'div',
43178                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43179                     cn : [
43180                         {
43181                             tag: 'div',
43182                             cls: this.hasFeedback ? 'has-feedback' : '',
43183                             cn: [
43184                                 input
43185                             ]
43186                         }
43187                     ]
43188                 }
43189             ]
43190             
43191         };
43192         
43193         if (this.fieldLabel.length) {
43194             var indicator = {
43195                 tag: 'i',
43196                 tooltip: 'This field is required'
43197             };
43198
43199             var label = {
43200                 tag: 'label',
43201                 'for':  id,
43202                 cls: 'control-label',
43203                 cn: []
43204             };
43205
43206             var label_text = {
43207                 tag: 'span',
43208                 html: this.fieldLabel
43209             };
43210
43211             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43212             label.cn = [
43213                 indicator,
43214                 label_text
43215             ];
43216
43217             if(this.indicatorpos == 'right') {
43218                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43219                 label.cn = [
43220                     label_text,
43221                     indicator
43222                 ];
43223             }
43224
43225             if(align == 'left') {
43226                 container = {
43227                     tag: 'div',
43228                     cn: [
43229                         container
43230                     ]
43231                 };
43232
43233                 if(this.labelWidth > 12){
43234                     label.style = "width: " + this.labelWidth + 'px';
43235                 }
43236                 if(this.labelWidth < 13 && this.labelmd == 0){
43237                     this.labelmd = this.labelWidth;
43238                 }
43239                 if(this.labellg > 0){
43240                     label.cls += ' col-lg-' + this.labellg;
43241                     input.cls += ' col-lg-' + (12 - this.labellg);
43242                 }
43243                 if(this.labelmd > 0){
43244                     label.cls += ' col-md-' + this.labelmd;
43245                     container.cls += ' col-md-' + (12 - this.labelmd);
43246                 }
43247                 if(this.labelsm > 0){
43248                     label.cls += ' col-sm-' + this.labelsm;
43249                     container.cls += ' col-sm-' + (12 - this.labelsm);
43250                 }
43251                 if(this.labelxs > 0){
43252                     label.cls += ' col-xs-' + this.labelxs;
43253                     container.cls += ' col-xs-' + (12 - this.labelxs);
43254                 }
43255             }
43256         }
43257
43258         cfg.cn = [
43259             label,
43260             container,
43261             hiddenInput
43262         ];
43263         
43264         var settings = this;
43265
43266         ['xs','sm','md','lg'].map(function(size){
43267             if (settings[size]) {
43268                 cfg.cls += ' col-' + size + '-' + settings[size];
43269             }
43270         });
43271         
43272         return cfg;
43273     },
43274     
43275     initEvents : function()
43276     {
43277         this.indicator = this.indicatorEl();
43278         
43279         this.initCurrencyEvent();
43280         
43281         this.initNumberEvent();
43282     },
43283     
43284     initCurrencyEvent : function()
43285     {
43286         if (!this.store) {
43287             throw "can not find store for combo";
43288         }
43289         
43290         this.store = Roo.factory(this.store, Roo.data);
43291         this.store.parent = this;
43292         
43293         this.createList();
43294         
43295         this.triggerEl = this.el.select('.input-group-addon', true).first();
43296         
43297         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43298         
43299         var _this = this;
43300         
43301         (function(){
43302             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43303             _this.list.setWidth(lw);
43304         }).defer(100);
43305         
43306         this.list.on('mouseover', this.onViewOver, this);
43307         this.list.on('mousemove', this.onViewMove, this);
43308         this.list.on('scroll', this.onViewScroll, this);
43309         
43310         if(!this.tpl){
43311             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43312         }
43313         
43314         this.view = new Roo.View(this.list, this.tpl, {
43315             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43316         });
43317         
43318         this.view.on('click', this.onViewClick, this);
43319         
43320         this.store.on('beforeload', this.onBeforeLoad, this);
43321         this.store.on('load', this.onLoad, this);
43322         this.store.on('loadexception', this.onLoadException, this);
43323         
43324         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43325             "up" : function(e){
43326                 this.inKeyMode = true;
43327                 this.selectPrev();
43328             },
43329
43330             "down" : function(e){
43331                 if(!this.isExpanded()){
43332                     this.onTriggerClick();
43333                 }else{
43334                     this.inKeyMode = true;
43335                     this.selectNext();
43336                 }
43337             },
43338
43339             "enter" : function(e){
43340                 this.collapse();
43341                 
43342                 if(this.fireEvent("specialkey", this, e)){
43343                     this.onViewClick(false);
43344                 }
43345                 
43346                 return true;
43347             },
43348
43349             "esc" : function(e){
43350                 this.collapse();
43351             },
43352
43353             "tab" : function(e){
43354                 this.collapse();
43355                 
43356                 if(this.fireEvent("specialkey", this, e)){
43357                     this.onViewClick(false);
43358                 }
43359                 
43360                 return true;
43361             },
43362
43363             scope : this,
43364
43365             doRelay : function(foo, bar, hname){
43366                 if(hname == 'down' || this.scope.isExpanded()){
43367                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43368                 }
43369                 return true;
43370             },
43371
43372             forceKeyDown: true
43373         });
43374         
43375         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43376         
43377     },
43378     
43379     initNumberEvent : function(e)
43380     {
43381         this.inputEl().on("keydown" , this.fireKey,  this);
43382         this.inputEl().on("focus", this.onFocus,  this);
43383         this.inputEl().on("blur", this.onBlur,  this);
43384         
43385         this.inputEl().relayEvent('keyup', this);
43386         
43387         if(this.indicator){
43388             this.indicator.addClass('invisible');
43389         }
43390  
43391         this.originalValue = this.getValue();
43392         
43393         if(this.validationEvent == 'keyup'){
43394             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43395             this.inputEl().on('keyup', this.filterValidation, this);
43396         }
43397         else if(this.validationEvent !== false){
43398             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43399         }
43400         
43401         if(this.selectOnFocus){
43402             this.on("focus", this.preFocus, this);
43403             
43404         }
43405         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43406             this.inputEl().on("keypress", this.filterKeys, this);
43407         } else {
43408             this.inputEl().relayEvent('keypress', this);
43409         }
43410         
43411         var allowed = "0123456789";
43412         
43413         if(this.allowDecimals){
43414             allowed += this.decimalSeparator;
43415         }
43416         
43417         if(this.allowNegative){
43418             allowed += "-";
43419         }
43420         
43421         if(this.thousandsDelimiter) {
43422             allowed += ",";
43423         }
43424         
43425         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43426         
43427         var keyPress = function(e){
43428             
43429             var k = e.getKey();
43430             
43431             var c = e.getCharCode();
43432             
43433             if(
43434                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43435                     allowed.indexOf(String.fromCharCode(c)) === -1
43436             ){
43437                 e.stopEvent();
43438                 return;
43439             }
43440             
43441             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43442                 return;
43443             }
43444             
43445             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43446                 e.stopEvent();
43447             }
43448         };
43449         
43450         this.inputEl().on("keypress", keyPress, this);
43451         
43452     },
43453     
43454     onTriggerClick : function(e)
43455     {   
43456         if(this.disabled){
43457             return;
43458         }
43459         
43460         this.page = 0;
43461         this.loadNext = false;
43462         
43463         if(this.isExpanded()){
43464             this.collapse();
43465             return;
43466         }
43467         
43468         this.hasFocus = true;
43469         
43470         if(this.triggerAction == 'all') {
43471             this.doQuery(this.allQuery, true);
43472             return;
43473         }
43474         
43475         this.doQuery(this.getRawValue());
43476     },
43477     
43478     getCurrency : function()
43479     {   
43480         var v = this.currencyEl().getValue();
43481         
43482         return v;
43483     },
43484     
43485     restrictHeight : function()
43486     {
43487         this.list.alignTo(this.currencyEl(), this.listAlign);
43488         this.list.alignTo(this.currencyEl(), this.listAlign);
43489     },
43490     
43491     onViewClick : function(view, doFocus, el, e)
43492     {
43493         var index = this.view.getSelectedIndexes()[0];
43494         
43495         var r = this.store.getAt(index);
43496         
43497         if(r){
43498             this.onSelect(r, index);
43499         }
43500     },
43501     
43502     onSelect : function(record, index){
43503         
43504         if(this.fireEvent('beforeselect', this, record, index) !== false){
43505         
43506             this.setFromCurrencyData(index > -1 ? record.data : false);
43507             
43508             this.collapse();
43509             
43510             this.fireEvent('select', this, record, index);
43511         }
43512     },
43513     
43514     setFromCurrencyData : function(o)
43515     {
43516         var currency = '';
43517         
43518         this.lastCurrency = o;
43519         
43520         if (this.currencyField) {
43521             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43522         } else {
43523             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43524         }
43525         
43526         this.lastSelectionText = currency;
43527         
43528         //setting default currency
43529         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43530             this.setCurrency(this.defaultCurrency);
43531             return;
43532         }
43533         
43534         this.setCurrency(currency);
43535     },
43536     
43537     setFromData : function(o)
43538     {
43539         var c = {};
43540         
43541         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43542         
43543         this.setFromCurrencyData(c);
43544         
43545         var value = '';
43546         
43547         if (this.name) {
43548             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43549         } else {
43550             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43551         }
43552         
43553         this.setValue(value);
43554         
43555     },
43556     
43557     setCurrency : function(v)
43558     {   
43559         this.currencyValue = v;
43560         
43561         if(this.rendered){
43562             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43563             this.validate();
43564         }
43565     },
43566     
43567     setValue : function(v)
43568     {
43569         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43570         
43571         this.value = v;
43572         
43573         if(this.rendered){
43574             
43575             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43576             
43577             this.inputEl().dom.value = (v == '') ? '' :
43578                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43579             
43580             if(!this.allowZero && v === '0') {
43581                 this.hiddenEl().dom.value = '';
43582                 this.inputEl().dom.value = '';
43583             }
43584             
43585             this.validate();
43586         }
43587     },
43588     
43589     getRawValue : function()
43590     {
43591         var v = this.inputEl().getValue();
43592         
43593         return v;
43594     },
43595     
43596     getValue : function()
43597     {
43598         return this.fixPrecision(this.parseValue(this.getRawValue()));
43599     },
43600     
43601     parseValue : function(value)
43602     {
43603         if(this.thousandsDelimiter) {
43604             value += "";
43605             r = new RegExp(",", "g");
43606             value = value.replace(r, "");
43607         }
43608         
43609         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43610         return isNaN(value) ? '' : value;
43611         
43612     },
43613     
43614     fixPrecision : function(value)
43615     {
43616         if(this.thousandsDelimiter) {
43617             value += "";
43618             r = new RegExp(",", "g");
43619             value = value.replace(r, "");
43620         }
43621         
43622         var nan = isNaN(value);
43623         
43624         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43625             return nan ? '' : value;
43626         }
43627         return parseFloat(value).toFixed(this.decimalPrecision);
43628     },
43629     
43630     decimalPrecisionFcn : function(v)
43631     {
43632         return Math.floor(v);
43633     },
43634     
43635     validateValue : function(value)
43636     {
43637         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43638             return false;
43639         }
43640         
43641         var num = this.parseValue(value);
43642         
43643         if(isNaN(num)){
43644             this.markInvalid(String.format(this.nanText, value));
43645             return false;
43646         }
43647         
43648         if(num < this.minValue){
43649             this.markInvalid(String.format(this.minText, this.minValue));
43650             return false;
43651         }
43652         
43653         if(num > this.maxValue){
43654             this.markInvalid(String.format(this.maxText, this.maxValue));
43655             return false;
43656         }
43657         
43658         return true;
43659     },
43660     
43661     validate : function()
43662     {
43663         if(this.disabled || this.allowBlank){
43664             this.markValid();
43665             return true;
43666         }
43667         
43668         var currency = this.getCurrency();
43669         
43670         if(this.validateValue(this.getRawValue()) && currency.length){
43671             this.markValid();
43672             return true;
43673         }
43674         
43675         this.markInvalid();
43676         return false;
43677     },
43678     
43679     getName: function()
43680     {
43681         return this.name;
43682     },
43683     
43684     beforeBlur : function()
43685     {
43686         if(!this.castInt){
43687             return;
43688         }
43689         
43690         var v = this.parseValue(this.getRawValue());
43691         
43692         if(v || v == 0){
43693             this.setValue(v);
43694         }
43695     },
43696     
43697     onBlur : function()
43698     {
43699         this.beforeBlur();
43700         
43701         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43702             //this.el.removeClass(this.focusClass);
43703         }
43704         
43705         this.hasFocus = false;
43706         
43707         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43708             this.validate();
43709         }
43710         
43711         var v = this.getValue();
43712         
43713         if(String(v) !== String(this.startValue)){
43714             this.fireEvent('change', this, v, this.startValue);
43715         }
43716         
43717         this.fireEvent("blur", this);
43718     },
43719     
43720     inputEl : function()
43721     {
43722         return this.el.select('.roo-money-amount-input', true).first();
43723     },
43724     
43725     currencyEl : function()
43726     {
43727         return this.el.select('.roo-money-currency-input', true).first();
43728     },
43729     
43730     hiddenEl : function()
43731     {
43732         return this.el.select('input.hidden-number-input',true).first();
43733     }
43734     
43735 });/**
43736  * @class Roo.bootstrap.BezierSignature
43737  * @extends Roo.bootstrap.Component
43738  * Bootstrap BezierSignature class
43739  * This script refer to:
43740  *    Title: Signature Pad
43741  *    Author: szimek
43742  *    Availability: https://github.com/szimek/signature_pad
43743  *
43744  * @constructor
43745  * Create a new BezierSignature
43746  * @param {Object} config The config object
43747  */
43748
43749 Roo.bootstrap.BezierSignature = function(config){
43750     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43751     this.addEvents({
43752         "resize" : true
43753     });
43754 };
43755
43756 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43757 {
43758      
43759     curve_data: [],
43760     
43761     is_empty: true,
43762     
43763     mouse_btn_down: true,
43764     
43765     /**
43766      * @cfg {int} canvas height
43767      */
43768     canvas_height: '200px',
43769     
43770     /**
43771      * @cfg {float|function} Radius of a single dot.
43772      */ 
43773     dot_size: false,
43774     
43775     /**
43776      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43777      */
43778     min_width: 0.5,
43779     
43780     /**
43781      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43782      */
43783     max_width: 2.5,
43784     
43785     /**
43786      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43787      */
43788     throttle: 16,
43789     
43790     /**
43791      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43792      */
43793     min_distance: 5,
43794     
43795     /**
43796      * @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.
43797      */
43798     bg_color: 'rgba(0, 0, 0, 0)',
43799     
43800     /**
43801      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43802      */
43803     dot_color: 'black',
43804     
43805     /**
43806      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43807      */ 
43808     velocity_filter_weight: 0.7,
43809     
43810     /**
43811      * @cfg {function} Callback when stroke begin. 
43812      */
43813     onBegin: false,
43814     
43815     /**
43816      * @cfg {function} Callback when stroke end.
43817      */
43818     onEnd: false,
43819     
43820     getAutoCreate : function()
43821     {
43822         var cls = 'roo-signature column';
43823         
43824         if(this.cls){
43825             cls += ' ' + this.cls;
43826         }
43827         
43828         var col_sizes = [
43829             'lg',
43830             'md',
43831             'sm',
43832             'xs'
43833         ];
43834         
43835         for(var i = 0; i < col_sizes.length; i++) {
43836             if(this[col_sizes[i]]) {
43837                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43838             }
43839         }
43840         
43841         var cfg = {
43842             tag: 'div',
43843             cls: cls,
43844             cn: [
43845                 {
43846                     tag: 'div',
43847                     cls: 'roo-signature-body',
43848                     cn: [
43849                         {
43850                             tag: 'canvas',
43851                             cls: 'roo-signature-body-canvas',
43852                             height: this.canvas_height,
43853                             width: this.canvas_width
43854                         }
43855                     ]
43856                 },
43857                 {
43858                     tag: 'input',
43859                     type: 'file',
43860                     style: 'display: none'
43861                 }
43862             ]
43863         };
43864         
43865         return cfg;
43866     },
43867     
43868     initEvents: function() 
43869     {
43870         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43871         
43872         var canvas = this.canvasEl();
43873         
43874         // mouse && touch event swapping...
43875         canvas.dom.style.touchAction = 'none';
43876         canvas.dom.style.msTouchAction = 'none';
43877         
43878         this.mouse_btn_down = false;
43879         canvas.on('mousedown', this._handleMouseDown, this);
43880         canvas.on('mousemove', this._handleMouseMove, this);
43881         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43882         
43883         if (window.PointerEvent) {
43884             canvas.on('pointerdown', this._handleMouseDown, this);
43885             canvas.on('pointermove', this._handleMouseMove, this);
43886             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43887         }
43888         
43889         if ('ontouchstart' in window) {
43890             canvas.on('touchstart', this._handleTouchStart, this);
43891             canvas.on('touchmove', this._handleTouchMove, this);
43892             canvas.on('touchend', this._handleTouchEnd, this);
43893         }
43894         
43895         Roo.EventManager.onWindowResize(this.resize, this, true);
43896         
43897         // file input event
43898         this.fileEl().on('change', this.uploadImage, this);
43899         
43900         this.clear();
43901         
43902         this.resize();
43903     },
43904     
43905     resize: function(){
43906         
43907         var canvas = this.canvasEl().dom;
43908         var ctx = this.canvasElCtx();
43909         var img_data = false;
43910         
43911         if(canvas.width > 0) {
43912             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43913         }
43914         // setting canvas width will clean img data
43915         canvas.width = 0;
43916         
43917         var style = window.getComputedStyle ? 
43918             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43919             
43920         var padding_left = parseInt(style.paddingLeft) || 0;
43921         var padding_right = parseInt(style.paddingRight) || 0;
43922         
43923         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43924         
43925         if(img_data) {
43926             ctx.putImageData(img_data, 0, 0);
43927         }
43928     },
43929     
43930     _handleMouseDown: function(e)
43931     {
43932         if (e.browserEvent.which === 1) {
43933             this.mouse_btn_down = true;
43934             this.strokeBegin(e);
43935         }
43936     },
43937     
43938     _handleMouseMove: function (e)
43939     {
43940         if (this.mouse_btn_down) {
43941             this.strokeMoveUpdate(e);
43942         }
43943     },
43944     
43945     _handleMouseUp: function (e)
43946     {
43947         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43948             this.mouse_btn_down = false;
43949             this.strokeEnd(e);
43950         }
43951     },
43952     
43953     _handleTouchStart: function (e) {
43954         
43955         e.preventDefault();
43956         if (e.browserEvent.targetTouches.length === 1) {
43957             // var touch = e.browserEvent.changedTouches[0];
43958             // this.strokeBegin(touch);
43959             
43960              this.strokeBegin(e); // assume e catching the correct xy...
43961         }
43962     },
43963     
43964     _handleTouchMove: function (e) {
43965         e.preventDefault();
43966         // var touch = event.targetTouches[0];
43967         // _this._strokeMoveUpdate(touch);
43968         this.strokeMoveUpdate(e);
43969     },
43970     
43971     _handleTouchEnd: function (e) {
43972         var wasCanvasTouched = e.target === this.canvasEl().dom;
43973         if (wasCanvasTouched) {
43974             e.preventDefault();
43975             // var touch = event.changedTouches[0];
43976             // _this._strokeEnd(touch);
43977             this.strokeEnd(e);
43978         }
43979     },
43980     
43981     reset: function () {
43982         this._lastPoints = [];
43983         this._lastVelocity = 0;
43984         this._lastWidth = (this.min_width + this.max_width) / 2;
43985         this.canvasElCtx().fillStyle = this.dot_color;
43986     },
43987     
43988     strokeMoveUpdate: function(e)
43989     {
43990         this.strokeUpdate(e);
43991         
43992         if (this.throttle) {
43993             this.throttleStroke(this.strokeUpdate, this.throttle);
43994         }
43995         else {
43996             this.strokeUpdate(e);
43997         }
43998     },
43999     
44000     strokeBegin: function(e)
44001     {
44002         var newPointGroup = {
44003             color: this.dot_color,
44004             points: []
44005         };
44006         
44007         if (typeof this.onBegin === 'function') {
44008             this.onBegin(e);
44009         }
44010         
44011         this.curve_data.push(newPointGroup);
44012         this.reset();
44013         this.strokeUpdate(e);
44014     },
44015     
44016     strokeUpdate: function(e)
44017     {
44018         var rect = this.canvasEl().dom.getBoundingClientRect();
44019         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44020         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44021         var lastPoints = lastPointGroup.points;
44022         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44023         var isLastPointTooClose = lastPoint
44024             ? point.distanceTo(lastPoint) <= this.min_distance
44025             : false;
44026         var color = lastPointGroup.color;
44027         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44028             var curve = this.addPoint(point);
44029             if (!lastPoint) {
44030                 this.drawDot({color: color, point: point});
44031             }
44032             else if (curve) {
44033                 this.drawCurve({color: color, curve: curve});
44034             }
44035             lastPoints.push({
44036                 time: point.time,
44037                 x: point.x,
44038                 y: point.y
44039             });
44040         }
44041     },
44042     
44043     strokeEnd: function(e)
44044     {
44045         this.strokeUpdate(e);
44046         if (typeof this.onEnd === 'function') {
44047             this.onEnd(e);
44048         }
44049     },
44050     
44051     addPoint:  function (point) {
44052         var _lastPoints = this._lastPoints;
44053         _lastPoints.push(point);
44054         if (_lastPoints.length > 2) {
44055             if (_lastPoints.length === 3) {
44056                 _lastPoints.unshift(_lastPoints[0]);
44057             }
44058             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44059             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44060             _lastPoints.shift();
44061             return curve;
44062         }
44063         return null;
44064     },
44065     
44066     calculateCurveWidths: function (startPoint, endPoint) {
44067         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44068             (1 - this.velocity_filter_weight) * this._lastVelocity;
44069
44070         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44071         var widths = {
44072             end: newWidth,
44073             start: this._lastWidth
44074         };
44075         
44076         this._lastVelocity = velocity;
44077         this._lastWidth = newWidth;
44078         return widths;
44079     },
44080     
44081     drawDot: function (_a) {
44082         var color = _a.color, point = _a.point;
44083         var ctx = this.canvasElCtx();
44084         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44085         ctx.beginPath();
44086         this.drawCurveSegment(point.x, point.y, width);
44087         ctx.closePath();
44088         ctx.fillStyle = color;
44089         ctx.fill();
44090     },
44091     
44092     drawCurve: function (_a) {
44093         var color = _a.color, curve = _a.curve;
44094         var ctx = this.canvasElCtx();
44095         var widthDelta = curve.endWidth - curve.startWidth;
44096         var drawSteps = Math.floor(curve.length()) * 2;
44097         ctx.beginPath();
44098         ctx.fillStyle = color;
44099         for (var i = 0; i < drawSteps; i += 1) {
44100         var t = i / drawSteps;
44101         var tt = t * t;
44102         var ttt = tt * t;
44103         var u = 1 - t;
44104         var uu = u * u;
44105         var uuu = uu * u;
44106         var x = uuu * curve.startPoint.x;
44107         x += 3 * uu * t * curve.control1.x;
44108         x += 3 * u * tt * curve.control2.x;
44109         x += ttt * curve.endPoint.x;
44110         var y = uuu * curve.startPoint.y;
44111         y += 3 * uu * t * curve.control1.y;
44112         y += 3 * u * tt * curve.control2.y;
44113         y += ttt * curve.endPoint.y;
44114         var width = curve.startWidth + ttt * widthDelta;
44115         this.drawCurveSegment(x, y, width);
44116         }
44117         ctx.closePath();
44118         ctx.fill();
44119     },
44120     
44121     drawCurveSegment: function (x, y, width) {
44122         var ctx = this.canvasElCtx();
44123         ctx.moveTo(x, y);
44124         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44125         this.is_empty = false;
44126     },
44127     
44128     clear: function()
44129     {
44130         var ctx = this.canvasElCtx();
44131         var canvas = this.canvasEl().dom;
44132         ctx.fillStyle = this.bg_color;
44133         ctx.clearRect(0, 0, canvas.width, canvas.height);
44134         ctx.fillRect(0, 0, canvas.width, canvas.height);
44135         this.curve_data = [];
44136         this.reset();
44137         this.is_empty = true;
44138     },
44139     
44140     fileEl: function()
44141     {
44142         return  this.el.select('input',true).first();
44143     },
44144     
44145     canvasEl: function()
44146     {
44147         return this.el.select('canvas',true).first();
44148     },
44149     
44150     canvasElCtx: function()
44151     {
44152         return this.el.select('canvas',true).first().dom.getContext('2d');
44153     },
44154     
44155     getImage: function(type)
44156     {
44157         if(this.is_empty) {
44158             return false;
44159         }
44160         
44161         // encryption ?
44162         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44163     },
44164     
44165     drawFromImage: function(img_src)
44166     {
44167         var img = new Image();
44168         
44169         img.onload = function(){
44170             this.canvasElCtx().drawImage(img, 0, 0);
44171         }.bind(this);
44172         
44173         img.src = img_src;
44174         
44175         this.is_empty = false;
44176     },
44177     
44178     selectImage: function()
44179     {
44180         this.fileEl().dom.click();
44181     },
44182     
44183     uploadImage: function(e)
44184     {
44185         var reader = new FileReader();
44186         
44187         reader.onload = function(e){
44188             var img = new Image();
44189             img.onload = function(){
44190                 this.reset();
44191                 this.canvasElCtx().drawImage(img, 0, 0);
44192             }.bind(this);
44193             img.src = e.target.result;
44194         }.bind(this);
44195         
44196         reader.readAsDataURL(e.target.files[0]);
44197     },
44198     
44199     // Bezier Point Constructor
44200     Point: (function () {
44201         function Point(x, y, time) {
44202             this.x = x;
44203             this.y = y;
44204             this.time = time || Date.now();
44205         }
44206         Point.prototype.distanceTo = function (start) {
44207             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44208         };
44209         Point.prototype.equals = function (other) {
44210             return this.x === other.x && this.y === other.y && this.time === other.time;
44211         };
44212         Point.prototype.velocityFrom = function (start) {
44213             return this.time !== start.time
44214             ? this.distanceTo(start) / (this.time - start.time)
44215             : 0;
44216         };
44217         return Point;
44218     }()),
44219     
44220     
44221     // Bezier Constructor
44222     Bezier: (function () {
44223         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44224             this.startPoint = startPoint;
44225             this.control2 = control2;
44226             this.control1 = control1;
44227             this.endPoint = endPoint;
44228             this.startWidth = startWidth;
44229             this.endWidth = endWidth;
44230         }
44231         Bezier.fromPoints = function (points, widths, scope) {
44232             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44233             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44234             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44235         };
44236         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44237             var dx1 = s1.x - s2.x;
44238             var dy1 = s1.y - s2.y;
44239             var dx2 = s2.x - s3.x;
44240             var dy2 = s2.y - s3.y;
44241             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44242             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44243             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44244             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44245             var dxm = m1.x - m2.x;
44246             var dym = m1.y - m2.y;
44247             var k = l2 / (l1 + l2);
44248             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44249             var tx = s2.x - cm.x;
44250             var ty = s2.y - cm.y;
44251             return {
44252                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44253                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44254             };
44255         };
44256         Bezier.prototype.length = function () {
44257             var steps = 10;
44258             var length = 0;
44259             var px;
44260             var py;
44261             for (var i = 0; i <= steps; i += 1) {
44262                 var t = i / steps;
44263                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44264                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44265                 if (i > 0) {
44266                     var xdiff = cx - px;
44267                     var ydiff = cy - py;
44268                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44269                 }
44270                 px = cx;
44271                 py = cy;
44272             }
44273             return length;
44274         };
44275         Bezier.prototype.point = function (t, start, c1, c2, end) {
44276             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44277             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44278             + (3.0 * c2 * (1.0 - t) * t * t)
44279             + (end * t * t * t);
44280         };
44281         return Bezier;
44282     }()),
44283     
44284     throttleStroke: function(fn, wait) {
44285       if (wait === void 0) { wait = 250; }
44286       var previous = 0;
44287       var timeout = null;
44288       var result;
44289       var storedContext;
44290       var storedArgs;
44291       var later = function () {
44292           previous = Date.now();
44293           timeout = null;
44294           result = fn.apply(storedContext, storedArgs);
44295           if (!timeout) {
44296               storedContext = null;
44297               storedArgs = [];
44298           }
44299       };
44300       return function wrapper() {
44301           var args = [];
44302           for (var _i = 0; _i < arguments.length; _i++) {
44303               args[_i] = arguments[_i];
44304           }
44305           var now = Date.now();
44306           var remaining = wait - (now - previous);
44307           storedContext = this;
44308           storedArgs = args;
44309           if (remaining <= 0 || remaining > wait) {
44310               if (timeout) {
44311                   clearTimeout(timeout);
44312                   timeout = null;
44313               }
44314               previous = now;
44315               result = fn.apply(storedContext, storedArgs);
44316               if (!timeout) {
44317                   storedContext = null;
44318                   storedArgs = [];
44319               }
44320           }
44321           else if (!timeout) {
44322               timeout = window.setTimeout(later, remaining);
44323           }
44324           return result;
44325       };
44326   }
44327   
44328 });
44329
44330  
44331
44332