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} placement how it is placed
19606  * @cfg {String} trigger click || hover (or false to trigger manually)
19607  * @cfg {String} over what (parent or false to trigger manually.)
19608  * @cfg {Number} delay - delay before showing
19609  
19610  * @constructor
19611  * Create a new Popover
19612  * @param {Object} config The config object
19613  */
19614
19615 Roo.bootstrap.Popover = function(config){
19616     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19617     
19618     this.addEvents({
19619         // raw events
19620          /**
19621          * @event show
19622          * After the popover show
19623          * 
19624          * @param {Roo.bootstrap.Popover} this
19625          */
19626         "show" : true,
19627         /**
19628          * @event hide
19629          * After the popover hide
19630          * 
19631          * @param {Roo.bootstrap.Popover} this
19632          */
19633         "hide" : true
19634     });
19635 };
19636
19637 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19638     
19639     title: 'Fill in a title',
19640     html: false,
19641     
19642     placement : 'right',
19643     trigger : 'hover', // hover
19644     
19645     delay : 0,
19646     
19647     over: 'parent',
19648     
19649     can_build_overlaid : false,
19650     
19651     getChildContainer : function()
19652     {
19653         return this.el.select('.popover-content',true).first();
19654     },
19655     
19656     getAutoCreate : function(){
19657          
19658         var cfg = {
19659            cls : 'popover roo-dynamic',
19660            style: 'display:block',
19661            cn : [
19662                 {
19663                     cls : 'arrow'
19664                 },
19665                 {
19666                     cls : 'popover-inner',
19667                     cn : [
19668                         {
19669                             tag: 'h3',
19670                             cls: 'popover-title popover-header',
19671                             html : this.title
19672                         },
19673                         {
19674                             cls : 'popover-content popover-body',
19675                             html : this.html
19676                         }
19677                     ]
19678                     
19679                 }
19680            ]
19681         };
19682         
19683         return cfg;
19684     },
19685     setTitle: function(str)
19686     {
19687         this.title = str;
19688         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19689     },
19690     setContent: function(str)
19691     {
19692         this.html = str;
19693         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19694     },
19695     // as it get's added to the bottom of the page.
19696     onRender : function(ct, position)
19697     {
19698         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19699         if(!this.el){
19700             var cfg = Roo.apply({},  this.getAutoCreate());
19701             cfg.id = Roo.id();
19702             
19703             if (this.cls) {
19704                 cfg.cls += ' ' + this.cls;
19705             }
19706             if (this.style) {
19707                 cfg.style = this.style;
19708             }
19709             //Roo.log("adding to ");
19710             this.el = Roo.get(document.body).createChild(cfg, position);
19711 //            Roo.log(this.el);
19712         }
19713         this.initEvents();
19714     },
19715     
19716     initEvents : function()
19717     {
19718         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19719         this.el.enableDisplayMode('block');
19720         this.el.hide();
19721         if (this.over === false) {
19722             return; 
19723         }
19724         if (this.triggers === false) {
19725             return;
19726         }
19727         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19728         var triggers = this.trigger ? this.trigger.split(' ') : [];
19729         Roo.each(triggers, function(trigger) {
19730         
19731             if (trigger == 'click') {
19732                 on_el.on('click', this.toggle, this);
19733             } else if (trigger != 'manual') {
19734                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19735                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19736       
19737                 on_el.on(eventIn  ,this.enter, this);
19738                 on_el.on(eventOut, this.leave, this);
19739             }
19740         }, this);
19741         
19742     },
19743     
19744     
19745     // private
19746     timeout : null,
19747     hoverState : null,
19748     
19749     toggle : function () {
19750         this.hoverState == 'in' ? this.leave() : this.enter();
19751     },
19752     
19753     enter : function () {
19754         
19755         clearTimeout(this.timeout);
19756     
19757         this.hoverState = 'in';
19758     
19759         if (!this.delay || !this.delay.show) {
19760             this.show();
19761             return;
19762         }
19763         var _t = this;
19764         this.timeout = setTimeout(function () {
19765             if (_t.hoverState == 'in') {
19766                 _t.show();
19767             }
19768         }, this.delay.show)
19769     },
19770     
19771     leave : function() {
19772         clearTimeout(this.timeout);
19773     
19774         this.hoverState = 'out';
19775     
19776         if (!this.delay || !this.delay.hide) {
19777             this.hide();
19778             return;
19779         }
19780         var _t = this;
19781         this.timeout = setTimeout(function () {
19782             if (_t.hoverState == 'out') {
19783                 _t.hide();
19784             }
19785         }, this.delay.hide)
19786     },
19787     
19788     show : function (on_el)
19789     {
19790         if (!on_el) {
19791             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19792         }
19793         
19794         // set content.
19795         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19796         if (this.html !== false) {
19797             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19798         }
19799         this.el.removeClass([
19800             'fade','top','bottom', 'left', 'right','in',
19801             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19802         ]);
19803         if (!this.title.length) {
19804             this.el.select('.popover-title',true).hide();
19805         }
19806         
19807         var placement = typeof this.placement == 'function' ?
19808             this.placement.call(this, this.el, on_el) :
19809             this.placement;
19810             
19811         var autoToken = /\s?auto?\s?/i;
19812         var autoPlace = autoToken.test(placement);
19813         if (autoPlace) {
19814             placement = placement.replace(autoToken, '') || 'top';
19815         }
19816         
19817         //this.el.detach()
19818         //this.el.setXY([0,0]);
19819         this.el.show();
19820         this.el.dom.style.display='block';
19821         this.el.addClass(placement);
19822         
19823         //this.el.appendTo(on_el);
19824         
19825         var p = this.getPosition();
19826         var box = this.el.getBox();
19827         
19828         if (autoPlace) {
19829             // fixme..
19830         }
19831         var align = Roo.bootstrap.Popover.alignment[placement];
19832         
19833 //        Roo.log(align);
19834         this.el.alignTo(on_el, align[0],align[1]);
19835         //var arrow = this.el.select('.arrow',true).first();
19836         //arrow.set(align[2], 
19837         
19838         this.el.addClass('in');
19839         
19840         
19841         if (this.el.hasClass('fade')) {
19842             // fade it?
19843         }
19844         
19845         this.hoverState = 'in';
19846         
19847         this.fireEvent('show', this);
19848         
19849     },
19850     hide : function()
19851     {
19852         this.el.setXY([0,0]);
19853         this.el.removeClass('in');
19854         this.el.hide();
19855         this.hoverState = null;
19856         
19857         this.fireEvent('hide', this);
19858     }
19859     
19860 });
19861
19862 Roo.bootstrap.Popover.alignment = {
19863     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19864     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19865     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19866     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19867 };
19868
19869  /*
19870  * - LGPL
19871  *
19872  * Progress
19873  * 
19874  */
19875
19876 /**
19877  * @class Roo.bootstrap.Progress
19878  * @extends Roo.bootstrap.Component
19879  * Bootstrap Progress class
19880  * @cfg {Boolean} striped striped of the progress bar
19881  * @cfg {Boolean} active animated of the progress bar
19882  * 
19883  * 
19884  * @constructor
19885  * Create a new Progress
19886  * @param {Object} config The config object
19887  */
19888
19889 Roo.bootstrap.Progress = function(config){
19890     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19891 };
19892
19893 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19894     
19895     striped : false,
19896     active: false,
19897     
19898     getAutoCreate : function(){
19899         var cfg = {
19900             tag: 'div',
19901             cls: 'progress'
19902         };
19903         
19904         
19905         if(this.striped){
19906             cfg.cls += ' progress-striped';
19907         }
19908       
19909         if(this.active){
19910             cfg.cls += ' active';
19911         }
19912         
19913         
19914         return cfg;
19915     }
19916    
19917 });
19918
19919  
19920
19921  /*
19922  * - LGPL
19923  *
19924  * ProgressBar
19925  * 
19926  */
19927
19928 /**
19929  * @class Roo.bootstrap.ProgressBar
19930  * @extends Roo.bootstrap.Component
19931  * Bootstrap ProgressBar class
19932  * @cfg {Number} aria_valuenow aria-value now
19933  * @cfg {Number} aria_valuemin aria-value min
19934  * @cfg {Number} aria_valuemax aria-value max
19935  * @cfg {String} label label for the progress bar
19936  * @cfg {String} panel (success | info | warning | danger )
19937  * @cfg {String} role role of the progress bar
19938  * @cfg {String} sr_only text
19939  * 
19940  * 
19941  * @constructor
19942  * Create a new ProgressBar
19943  * @param {Object} config The config object
19944  */
19945
19946 Roo.bootstrap.ProgressBar = function(config){
19947     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19948 };
19949
19950 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19951     
19952     aria_valuenow : 0,
19953     aria_valuemin : 0,
19954     aria_valuemax : 100,
19955     label : false,
19956     panel : false,
19957     role : false,
19958     sr_only: false,
19959     
19960     getAutoCreate : function()
19961     {
19962         
19963         var cfg = {
19964             tag: 'div',
19965             cls: 'progress-bar',
19966             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19967         };
19968         
19969         if(this.sr_only){
19970             cfg.cn = {
19971                 tag: 'span',
19972                 cls: 'sr-only',
19973                 html: this.sr_only
19974             }
19975         }
19976         
19977         if(this.role){
19978             cfg.role = this.role;
19979         }
19980         
19981         if(this.aria_valuenow){
19982             cfg['aria-valuenow'] = this.aria_valuenow;
19983         }
19984         
19985         if(this.aria_valuemin){
19986             cfg['aria-valuemin'] = this.aria_valuemin;
19987         }
19988         
19989         if(this.aria_valuemax){
19990             cfg['aria-valuemax'] = this.aria_valuemax;
19991         }
19992         
19993         if(this.label && !this.sr_only){
19994             cfg.html = this.label;
19995         }
19996         
19997         if(this.panel){
19998             cfg.cls += ' progress-bar-' + this.panel;
19999         }
20000         
20001         return cfg;
20002     },
20003     
20004     update : function(aria_valuenow)
20005     {
20006         this.aria_valuenow = aria_valuenow;
20007         
20008         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20009     }
20010    
20011 });
20012
20013  
20014
20015  /*
20016  * - LGPL
20017  *
20018  * column
20019  * 
20020  */
20021
20022 /**
20023  * @class Roo.bootstrap.TabGroup
20024  * @extends Roo.bootstrap.Column
20025  * Bootstrap Column class
20026  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20027  * @cfg {Boolean} carousel true to make the group behave like a carousel
20028  * @cfg {Boolean} bullets show bullets for the panels
20029  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20030  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20031  * @cfg {Boolean} showarrow (true|false) show arrow default true
20032  * 
20033  * @constructor
20034  * Create a new TabGroup
20035  * @param {Object} config The config object
20036  */
20037
20038 Roo.bootstrap.TabGroup = function(config){
20039     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20040     if (!this.navId) {
20041         this.navId = Roo.id();
20042     }
20043     this.tabs = [];
20044     Roo.bootstrap.TabGroup.register(this);
20045     
20046 };
20047
20048 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20049     
20050     carousel : false,
20051     transition : false,
20052     bullets : 0,
20053     timer : 0,
20054     autoslide : false,
20055     slideFn : false,
20056     slideOnTouch : false,
20057     showarrow : true,
20058     
20059     getAutoCreate : function()
20060     {
20061         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20062         
20063         cfg.cls += ' tab-content';
20064         
20065         if (this.carousel) {
20066             cfg.cls += ' carousel slide';
20067             
20068             cfg.cn = [{
20069                cls : 'carousel-inner',
20070                cn : []
20071             }];
20072         
20073             if(this.bullets  && !Roo.isTouch){
20074                 
20075                 var bullets = {
20076                     cls : 'carousel-bullets',
20077                     cn : []
20078                 };
20079                
20080                 if(this.bullets_cls){
20081                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20082                 }
20083                 
20084                 bullets.cn.push({
20085                     cls : 'clear'
20086                 });
20087                 
20088                 cfg.cn[0].cn.push(bullets);
20089             }
20090             
20091             if(this.showarrow){
20092                 cfg.cn[0].cn.push({
20093                     tag : 'div',
20094                     class : 'carousel-arrow',
20095                     cn : [
20096                         {
20097                             tag : 'div',
20098                             class : 'carousel-prev',
20099                             cn : [
20100                                 {
20101                                     tag : 'i',
20102                                     class : 'fa fa-chevron-left'
20103                                 }
20104                             ]
20105                         },
20106                         {
20107                             tag : 'div',
20108                             class : 'carousel-next',
20109                             cn : [
20110                                 {
20111                                     tag : 'i',
20112                                     class : 'fa fa-chevron-right'
20113                                 }
20114                             ]
20115                         }
20116                     ]
20117                 });
20118             }
20119             
20120         }
20121         
20122         return cfg;
20123     },
20124     
20125     initEvents:  function()
20126     {
20127 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20128 //            this.el.on("touchstart", this.onTouchStart, this);
20129 //        }
20130         
20131         if(this.autoslide){
20132             var _this = this;
20133             
20134             this.slideFn = window.setInterval(function() {
20135                 _this.showPanelNext();
20136             }, this.timer);
20137         }
20138         
20139         if(this.showarrow){
20140             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20141             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20142         }
20143         
20144         
20145     },
20146     
20147 //    onTouchStart : function(e, el, o)
20148 //    {
20149 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20150 //            return;
20151 //        }
20152 //        
20153 //        this.showPanelNext();
20154 //    },
20155     
20156     
20157     getChildContainer : function()
20158     {
20159         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20160     },
20161     
20162     /**
20163     * register a Navigation item
20164     * @param {Roo.bootstrap.NavItem} the navitem to add
20165     */
20166     register : function(item)
20167     {
20168         this.tabs.push( item);
20169         item.navId = this.navId; // not really needed..
20170         this.addBullet();
20171     
20172     },
20173     
20174     getActivePanel : function()
20175     {
20176         var r = false;
20177         Roo.each(this.tabs, function(t) {
20178             if (t.active) {
20179                 r = t;
20180                 return false;
20181             }
20182             return null;
20183         });
20184         return r;
20185         
20186     },
20187     getPanelByName : function(n)
20188     {
20189         var r = false;
20190         Roo.each(this.tabs, function(t) {
20191             if (t.tabId == n) {
20192                 r = t;
20193                 return false;
20194             }
20195             return null;
20196         });
20197         return r;
20198     },
20199     indexOfPanel : function(p)
20200     {
20201         var r = false;
20202         Roo.each(this.tabs, function(t,i) {
20203             if (t.tabId == p.tabId) {
20204                 r = i;
20205                 return false;
20206             }
20207             return null;
20208         });
20209         return r;
20210     },
20211     /**
20212      * show a specific panel
20213      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20214      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20215      */
20216     showPanel : function (pan)
20217     {
20218         if(this.transition || typeof(pan) == 'undefined'){
20219             Roo.log("waiting for the transitionend");
20220             return false;
20221         }
20222         
20223         if (typeof(pan) == 'number') {
20224             pan = this.tabs[pan];
20225         }
20226         
20227         if (typeof(pan) == 'string') {
20228             pan = this.getPanelByName(pan);
20229         }
20230         
20231         var cur = this.getActivePanel();
20232         
20233         if(!pan || !cur){
20234             Roo.log('pan or acitve pan is undefined');
20235             return false;
20236         }
20237         
20238         if (pan.tabId == this.getActivePanel().tabId) {
20239             return true;
20240         }
20241         
20242         if (false === cur.fireEvent('beforedeactivate')) {
20243             return false;
20244         }
20245         
20246         if(this.bullets > 0 && !Roo.isTouch){
20247             this.setActiveBullet(this.indexOfPanel(pan));
20248         }
20249         
20250         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20251             
20252             //class="carousel-item carousel-item-next carousel-item-left"
20253             
20254             this.transition = true;
20255             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20256             var lr = dir == 'next' ? 'left' : 'right';
20257             pan.el.addClass(dir); // or prev
20258             pan.el.addClass('carousel-item-' + dir); // or prev
20259             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20260             cur.el.addClass(lr); // or right
20261             pan.el.addClass(lr);
20262             cur.el.addClass('carousel-item-' +lr); // or right
20263             pan.el.addClass('carousel-item-' +lr);
20264             
20265             
20266             var _this = this;
20267             cur.el.on('transitionend', function() {
20268                 Roo.log("trans end?");
20269                 
20270                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20271                 pan.setActive(true);
20272                 
20273                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20274                 cur.setActive(false);
20275                 
20276                 _this.transition = false;
20277                 
20278             }, this, { single:  true } );
20279             
20280             return true;
20281         }
20282         
20283         cur.setActive(false);
20284         pan.setActive(true);
20285         
20286         return true;
20287         
20288     },
20289     showPanelNext : function()
20290     {
20291         var i = this.indexOfPanel(this.getActivePanel());
20292         
20293         if (i >= this.tabs.length - 1 && !this.autoslide) {
20294             return;
20295         }
20296         
20297         if (i >= this.tabs.length - 1 && this.autoslide) {
20298             i = -1;
20299         }
20300         
20301         this.showPanel(this.tabs[i+1]);
20302     },
20303     
20304     showPanelPrev : function()
20305     {
20306         var i = this.indexOfPanel(this.getActivePanel());
20307         
20308         if (i  < 1 && !this.autoslide) {
20309             return;
20310         }
20311         
20312         if (i < 1 && this.autoslide) {
20313             i = this.tabs.length;
20314         }
20315         
20316         this.showPanel(this.tabs[i-1]);
20317     },
20318     
20319     
20320     addBullet: function()
20321     {
20322         if(!this.bullets || Roo.isTouch){
20323             return;
20324         }
20325         var ctr = this.el.select('.carousel-bullets',true).first();
20326         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20327         var bullet = ctr.createChild({
20328             cls : 'bullet bullet-' + i
20329         },ctr.dom.lastChild);
20330         
20331         
20332         var _this = this;
20333         
20334         bullet.on('click', (function(e, el, o, ii, t){
20335
20336             e.preventDefault();
20337
20338             this.showPanel(ii);
20339
20340             if(this.autoslide && this.slideFn){
20341                 clearInterval(this.slideFn);
20342                 this.slideFn = window.setInterval(function() {
20343                     _this.showPanelNext();
20344                 }, this.timer);
20345             }
20346
20347         }).createDelegate(this, [i, bullet], true));
20348                 
20349         
20350     },
20351      
20352     setActiveBullet : function(i)
20353     {
20354         if(Roo.isTouch){
20355             return;
20356         }
20357         
20358         Roo.each(this.el.select('.bullet', true).elements, function(el){
20359             el.removeClass('selected');
20360         });
20361
20362         var bullet = this.el.select('.bullet-' + i, true).first();
20363         
20364         if(!bullet){
20365             return;
20366         }
20367         
20368         bullet.addClass('selected');
20369     }
20370     
20371     
20372   
20373 });
20374
20375  
20376
20377  
20378  
20379 Roo.apply(Roo.bootstrap.TabGroup, {
20380     
20381     groups: {},
20382      /**
20383     * register a Navigation Group
20384     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20385     */
20386     register : function(navgrp)
20387     {
20388         this.groups[navgrp.navId] = navgrp;
20389         
20390     },
20391     /**
20392     * fetch a Navigation Group based on the navigation ID
20393     * if one does not exist , it will get created.
20394     * @param {string} the navgroup to add
20395     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20396     */
20397     get: function(navId) {
20398         if (typeof(this.groups[navId]) == 'undefined') {
20399             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20400         }
20401         return this.groups[navId] ;
20402     }
20403     
20404     
20405     
20406 });
20407
20408  /*
20409  * - LGPL
20410  *
20411  * TabPanel
20412  * 
20413  */
20414
20415 /**
20416  * @class Roo.bootstrap.TabPanel
20417  * @extends Roo.bootstrap.Component
20418  * Bootstrap TabPanel class
20419  * @cfg {Boolean} active panel active
20420  * @cfg {String} html panel content
20421  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20422  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20423  * @cfg {String} href click to link..
20424  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20425  * 
20426  * 
20427  * @constructor
20428  * Create a new TabPanel
20429  * @param {Object} config The config object
20430  */
20431
20432 Roo.bootstrap.TabPanel = function(config){
20433     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20434     this.addEvents({
20435         /**
20436              * @event changed
20437              * Fires when the active status changes
20438              * @param {Roo.bootstrap.TabPanel} this
20439              * @param {Boolean} state the new state
20440             
20441          */
20442         'changed': true,
20443         /**
20444              * @event beforedeactivate
20445              * Fires before a tab is de-activated - can be used to do validation on a form.
20446              * @param {Roo.bootstrap.TabPanel} this
20447              * @return {Boolean} false if there is an error
20448             
20449          */
20450         'beforedeactivate': true
20451      });
20452     
20453     this.tabId = this.tabId || Roo.id();
20454   
20455 };
20456
20457 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20458     
20459     active: false,
20460     html: false,
20461     tabId: false,
20462     navId : false,
20463     href : '',
20464     touchSlide : false,
20465     getAutoCreate : function(){
20466         
20467         
20468         var cfg = {
20469             tag: 'div',
20470             // item is needed for carousel - not sure if it has any effect otherwise
20471             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20472             html: this.html || ''
20473         };
20474         
20475         if(this.active){
20476             cfg.cls += ' active';
20477         }
20478         
20479         if(this.tabId){
20480             cfg.tabId = this.tabId;
20481         }
20482         
20483         
20484         
20485         return cfg;
20486     },
20487     
20488     initEvents:  function()
20489     {
20490         var p = this.parent();
20491         
20492         this.navId = this.navId || p.navId;
20493         
20494         if (typeof(this.navId) != 'undefined') {
20495             // not really needed.. but just in case.. parent should be a NavGroup.
20496             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20497             
20498             tg.register(this);
20499             
20500             var i = tg.tabs.length - 1;
20501             
20502             if(this.active && tg.bullets > 0 && i < tg.bullets){
20503                 tg.setActiveBullet(i);
20504             }
20505         }
20506         
20507         this.el.on('click', this.onClick, this);
20508         
20509         if(Roo.isTouch && this.touchSlide){
20510             this.el.on("touchstart", this.onTouchStart, this);
20511             this.el.on("touchmove", this.onTouchMove, this);
20512             this.el.on("touchend", this.onTouchEnd, this);
20513         }
20514         
20515     },
20516     
20517     onRender : function(ct, position)
20518     {
20519         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20520     },
20521     
20522     setActive : function(state)
20523     {
20524         Roo.log("panel - set active " + this.tabId + "=" + state);
20525         
20526         this.active = state;
20527         if (!state) {
20528             this.el.removeClass('active');
20529             
20530         } else  if (!this.el.hasClass('active')) {
20531             this.el.addClass('active');
20532         }
20533         
20534         this.fireEvent('changed', this, state);
20535     },
20536     
20537     onClick : function(e)
20538     {
20539         e.preventDefault();
20540         
20541         if(!this.href.length){
20542             return;
20543         }
20544         
20545         window.location.href = this.href;
20546     },
20547     
20548     startX : 0,
20549     startY : 0,
20550     endX : 0,
20551     endY : 0,
20552     swiping : false,
20553     
20554     onTouchStart : function(e)
20555     {
20556         this.swiping = false;
20557         
20558         this.startX = e.browserEvent.touches[0].clientX;
20559         this.startY = e.browserEvent.touches[0].clientY;
20560     },
20561     
20562     onTouchMove : function(e)
20563     {
20564         this.swiping = true;
20565         
20566         this.endX = e.browserEvent.touches[0].clientX;
20567         this.endY = e.browserEvent.touches[0].clientY;
20568     },
20569     
20570     onTouchEnd : function(e)
20571     {
20572         if(!this.swiping){
20573             this.onClick(e);
20574             return;
20575         }
20576         
20577         var tabGroup = this.parent();
20578         
20579         if(this.endX > this.startX){ // swiping right
20580             tabGroup.showPanelPrev();
20581             return;
20582         }
20583         
20584         if(this.startX > this.endX){ // swiping left
20585             tabGroup.showPanelNext();
20586             return;
20587         }
20588     }
20589     
20590     
20591 });
20592  
20593
20594  
20595
20596  /*
20597  * - LGPL
20598  *
20599  * DateField
20600  * 
20601  */
20602
20603 /**
20604  * @class Roo.bootstrap.DateField
20605  * @extends Roo.bootstrap.Input
20606  * Bootstrap DateField class
20607  * @cfg {Number} weekStart default 0
20608  * @cfg {String} viewMode default empty, (months|years)
20609  * @cfg {String} minViewMode default empty, (months|years)
20610  * @cfg {Number} startDate default -Infinity
20611  * @cfg {Number} endDate default Infinity
20612  * @cfg {Boolean} todayHighlight default false
20613  * @cfg {Boolean} todayBtn default false
20614  * @cfg {Boolean} calendarWeeks default false
20615  * @cfg {Object} daysOfWeekDisabled default empty
20616  * @cfg {Boolean} singleMode default false (true | false)
20617  * 
20618  * @cfg {Boolean} keyboardNavigation default true
20619  * @cfg {String} language default en
20620  * 
20621  * @constructor
20622  * Create a new DateField
20623  * @param {Object} config The config object
20624  */
20625
20626 Roo.bootstrap.DateField = function(config){
20627     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20628      this.addEvents({
20629             /**
20630              * @event show
20631              * Fires when this field show.
20632              * @param {Roo.bootstrap.DateField} this
20633              * @param {Mixed} date The date value
20634              */
20635             show : true,
20636             /**
20637              * @event show
20638              * Fires when this field hide.
20639              * @param {Roo.bootstrap.DateField} this
20640              * @param {Mixed} date The date value
20641              */
20642             hide : true,
20643             /**
20644              * @event select
20645              * Fires when select a date.
20646              * @param {Roo.bootstrap.DateField} this
20647              * @param {Mixed} date The date value
20648              */
20649             select : true,
20650             /**
20651              * @event beforeselect
20652              * Fires when before select a date.
20653              * @param {Roo.bootstrap.DateField} this
20654              * @param {Mixed} date The date value
20655              */
20656             beforeselect : true
20657         });
20658 };
20659
20660 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20661     
20662     /**
20663      * @cfg {String} format
20664      * The default date format string which can be overriden for localization support.  The format must be
20665      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20666      */
20667     format : "m/d/y",
20668     /**
20669      * @cfg {String} altFormats
20670      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20671      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20672      */
20673     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20674     
20675     weekStart : 0,
20676     
20677     viewMode : '',
20678     
20679     minViewMode : '',
20680     
20681     todayHighlight : false,
20682     
20683     todayBtn: false,
20684     
20685     language: 'en',
20686     
20687     keyboardNavigation: true,
20688     
20689     calendarWeeks: false,
20690     
20691     startDate: -Infinity,
20692     
20693     endDate: Infinity,
20694     
20695     daysOfWeekDisabled: [],
20696     
20697     _events: [],
20698     
20699     singleMode : false,
20700     
20701     UTCDate: function()
20702     {
20703         return new Date(Date.UTC.apply(Date, arguments));
20704     },
20705     
20706     UTCToday: function()
20707     {
20708         var today = new Date();
20709         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20710     },
20711     
20712     getDate: function() {
20713             var d = this.getUTCDate();
20714             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20715     },
20716     
20717     getUTCDate: function() {
20718             return this.date;
20719     },
20720     
20721     setDate: function(d) {
20722             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20723     },
20724     
20725     setUTCDate: function(d) {
20726             this.date = d;
20727             this.setValue(this.formatDate(this.date));
20728     },
20729         
20730     onRender: function(ct, position)
20731     {
20732         
20733         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20734         
20735         this.language = this.language || 'en';
20736         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20737         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20738         
20739         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20740         this.format = this.format || 'm/d/y';
20741         this.isInline = false;
20742         this.isInput = true;
20743         this.component = this.el.select('.add-on', true).first() || false;
20744         this.component = (this.component && this.component.length === 0) ? false : this.component;
20745         this.hasInput = this.component && this.inputEl().length;
20746         
20747         if (typeof(this.minViewMode === 'string')) {
20748             switch (this.minViewMode) {
20749                 case 'months':
20750                     this.minViewMode = 1;
20751                     break;
20752                 case 'years':
20753                     this.minViewMode = 2;
20754                     break;
20755                 default:
20756                     this.minViewMode = 0;
20757                     break;
20758             }
20759         }
20760         
20761         if (typeof(this.viewMode === 'string')) {
20762             switch (this.viewMode) {
20763                 case 'months':
20764                     this.viewMode = 1;
20765                     break;
20766                 case 'years':
20767                     this.viewMode = 2;
20768                     break;
20769                 default:
20770                     this.viewMode = 0;
20771                     break;
20772             }
20773         }
20774                 
20775         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20776         
20777 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20778         
20779         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20780         
20781         this.picker().on('mousedown', this.onMousedown, this);
20782         this.picker().on('click', this.onClick, this);
20783         
20784         this.picker().addClass('datepicker-dropdown');
20785         
20786         this.startViewMode = this.viewMode;
20787         
20788         if(this.singleMode){
20789             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20790                 v.setVisibilityMode(Roo.Element.DISPLAY);
20791                 v.hide();
20792             });
20793             
20794             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20795                 v.setStyle('width', '189px');
20796             });
20797         }
20798         
20799         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20800             if(!this.calendarWeeks){
20801                 v.remove();
20802                 return;
20803             }
20804             
20805             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20806             v.attr('colspan', function(i, val){
20807                 return parseInt(val) + 1;
20808             });
20809         });
20810                         
20811         
20812         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20813         
20814         this.setStartDate(this.startDate);
20815         this.setEndDate(this.endDate);
20816         
20817         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20818         
20819         this.fillDow();
20820         this.fillMonths();
20821         this.update();
20822         this.showMode();
20823         
20824         if(this.isInline) {
20825             this.showPopup();
20826         }
20827     },
20828     
20829     picker : function()
20830     {
20831         return this.pickerEl;
20832 //        return this.el.select('.datepicker', true).first();
20833     },
20834     
20835     fillDow: function()
20836     {
20837         var dowCnt = this.weekStart;
20838         
20839         var dow = {
20840             tag: 'tr',
20841             cn: [
20842                 
20843             ]
20844         };
20845         
20846         if(this.calendarWeeks){
20847             dow.cn.push({
20848                 tag: 'th',
20849                 cls: 'cw',
20850                 html: '&nbsp;'
20851             })
20852         }
20853         
20854         while (dowCnt < this.weekStart + 7) {
20855             dow.cn.push({
20856                 tag: 'th',
20857                 cls: 'dow',
20858                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20859             });
20860         }
20861         
20862         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20863     },
20864     
20865     fillMonths: function()
20866     {    
20867         var i = 0;
20868         var months = this.picker().select('>.datepicker-months td', true).first();
20869         
20870         months.dom.innerHTML = '';
20871         
20872         while (i < 12) {
20873             var month = {
20874                 tag: 'span',
20875                 cls: 'month',
20876                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20877             };
20878             
20879             months.createChild(month);
20880         }
20881         
20882     },
20883     
20884     update: function()
20885     {
20886         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;
20887         
20888         if (this.date < this.startDate) {
20889             this.viewDate = new Date(this.startDate);
20890         } else if (this.date > this.endDate) {
20891             this.viewDate = new Date(this.endDate);
20892         } else {
20893             this.viewDate = new Date(this.date);
20894         }
20895         
20896         this.fill();
20897     },
20898     
20899     fill: function() 
20900     {
20901         var d = new Date(this.viewDate),
20902                 year = d.getUTCFullYear(),
20903                 month = d.getUTCMonth(),
20904                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20905                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20906                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20907                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20908                 currentDate = this.date && this.date.valueOf(),
20909                 today = this.UTCToday();
20910         
20911         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20912         
20913 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20914         
20915 //        this.picker.select('>tfoot th.today').
20916 //                                              .text(dates[this.language].today)
20917 //                                              .toggle(this.todayBtn !== false);
20918     
20919         this.updateNavArrows();
20920         this.fillMonths();
20921                                                 
20922         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20923         
20924         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20925          
20926         prevMonth.setUTCDate(day);
20927         
20928         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20929         
20930         var nextMonth = new Date(prevMonth);
20931         
20932         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20933         
20934         nextMonth = nextMonth.valueOf();
20935         
20936         var fillMonths = false;
20937         
20938         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20939         
20940         while(prevMonth.valueOf() <= nextMonth) {
20941             var clsName = '';
20942             
20943             if (prevMonth.getUTCDay() === this.weekStart) {
20944                 if(fillMonths){
20945                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20946                 }
20947                     
20948                 fillMonths = {
20949                     tag: 'tr',
20950                     cn: []
20951                 };
20952                 
20953                 if(this.calendarWeeks){
20954                     // ISO 8601: First week contains first thursday.
20955                     // ISO also states week starts on Monday, but we can be more abstract here.
20956                     var
20957                     // Start of current week: based on weekstart/current date
20958                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20959                     // Thursday of this week
20960                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20961                     // First Thursday of year, year from thursday
20962                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20963                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20964                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20965                     
20966                     fillMonths.cn.push({
20967                         tag: 'td',
20968                         cls: 'cw',
20969                         html: calWeek
20970                     });
20971                 }
20972             }
20973             
20974             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20975                 clsName += ' old';
20976             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20977                 clsName += ' new';
20978             }
20979             if (this.todayHighlight &&
20980                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20981                 prevMonth.getUTCMonth() == today.getMonth() &&
20982                 prevMonth.getUTCDate() == today.getDate()) {
20983                 clsName += ' today';
20984             }
20985             
20986             if (currentDate && prevMonth.valueOf() === currentDate) {
20987                 clsName += ' active';
20988             }
20989             
20990             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20991                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20992                     clsName += ' disabled';
20993             }
20994             
20995             fillMonths.cn.push({
20996                 tag: 'td',
20997                 cls: 'day ' + clsName,
20998                 html: prevMonth.getDate()
20999             });
21000             
21001             prevMonth.setDate(prevMonth.getDate()+1);
21002         }
21003           
21004         var currentYear = this.date && this.date.getUTCFullYear();
21005         var currentMonth = this.date && this.date.getUTCMonth();
21006         
21007         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21008         
21009         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21010             v.removeClass('active');
21011             
21012             if(currentYear === year && k === currentMonth){
21013                 v.addClass('active');
21014             }
21015             
21016             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21017                 v.addClass('disabled');
21018             }
21019             
21020         });
21021         
21022         
21023         year = parseInt(year/10, 10) * 10;
21024         
21025         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21026         
21027         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21028         
21029         year -= 1;
21030         for (var i = -1; i < 11; i++) {
21031             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21032                 tag: 'span',
21033                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21034                 html: year
21035             });
21036             
21037             year += 1;
21038         }
21039     },
21040     
21041     showMode: function(dir) 
21042     {
21043         if (dir) {
21044             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21045         }
21046         
21047         Roo.each(this.picker().select('>div',true).elements, function(v){
21048             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21049             v.hide();
21050         });
21051         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21052     },
21053     
21054     place: function()
21055     {
21056         if(this.isInline) {
21057             return;
21058         }
21059         
21060         this.picker().removeClass(['bottom', 'top']);
21061         
21062         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21063             /*
21064              * place to the top of element!
21065              *
21066              */
21067             
21068             this.picker().addClass('top');
21069             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21070             
21071             return;
21072         }
21073         
21074         this.picker().addClass('bottom');
21075         
21076         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21077     },
21078     
21079     parseDate : function(value)
21080     {
21081         if(!value || value instanceof Date){
21082             return value;
21083         }
21084         var v = Date.parseDate(value, this.format);
21085         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21086             v = Date.parseDate(value, 'Y-m-d');
21087         }
21088         if(!v && this.altFormats){
21089             if(!this.altFormatsArray){
21090                 this.altFormatsArray = this.altFormats.split("|");
21091             }
21092             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21093                 v = Date.parseDate(value, this.altFormatsArray[i]);
21094             }
21095         }
21096         return v;
21097     },
21098     
21099     formatDate : function(date, fmt)
21100     {   
21101         return (!date || !(date instanceof Date)) ?
21102         date : date.dateFormat(fmt || this.format);
21103     },
21104     
21105     onFocus : function()
21106     {
21107         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21108         this.showPopup();
21109     },
21110     
21111     onBlur : function()
21112     {
21113         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21114         
21115         var d = this.inputEl().getValue();
21116         
21117         this.setValue(d);
21118                 
21119         this.hidePopup();
21120     },
21121     
21122     showPopup : function()
21123     {
21124         this.picker().show();
21125         this.update();
21126         this.place();
21127         
21128         this.fireEvent('showpopup', this, this.date);
21129     },
21130     
21131     hidePopup : function()
21132     {
21133         if(this.isInline) {
21134             return;
21135         }
21136         this.picker().hide();
21137         this.viewMode = this.startViewMode;
21138         this.showMode();
21139         
21140         this.fireEvent('hidepopup', this, this.date);
21141         
21142     },
21143     
21144     onMousedown: function(e)
21145     {
21146         e.stopPropagation();
21147         e.preventDefault();
21148     },
21149     
21150     keyup: function(e)
21151     {
21152         Roo.bootstrap.DateField.superclass.keyup.call(this);
21153         this.update();
21154     },
21155
21156     setValue: function(v)
21157     {
21158         if(this.fireEvent('beforeselect', this, v) !== false){
21159             var d = new Date(this.parseDate(v) ).clearTime();
21160         
21161             if(isNaN(d.getTime())){
21162                 this.date = this.viewDate = '';
21163                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21164                 return;
21165             }
21166
21167             v = this.formatDate(d);
21168
21169             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21170
21171             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21172
21173             this.update();
21174
21175             this.fireEvent('select', this, this.date);
21176         }
21177     },
21178     
21179     getValue: function()
21180     {
21181         return this.formatDate(this.date);
21182     },
21183     
21184     fireKey: function(e)
21185     {
21186         if (!this.picker().isVisible()){
21187             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21188                 this.showPopup();
21189             }
21190             return;
21191         }
21192         
21193         var dateChanged = false,
21194         dir, day, month,
21195         newDate, newViewDate;
21196         
21197         switch(e.keyCode){
21198             case 27: // escape
21199                 this.hidePopup();
21200                 e.preventDefault();
21201                 break;
21202             case 37: // left
21203             case 39: // right
21204                 if (!this.keyboardNavigation) {
21205                     break;
21206                 }
21207                 dir = e.keyCode == 37 ? -1 : 1;
21208                 
21209                 if (e.ctrlKey){
21210                     newDate = this.moveYear(this.date, dir);
21211                     newViewDate = this.moveYear(this.viewDate, dir);
21212                 } else if (e.shiftKey){
21213                     newDate = this.moveMonth(this.date, dir);
21214                     newViewDate = this.moveMonth(this.viewDate, dir);
21215                 } else {
21216                     newDate = new Date(this.date);
21217                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21218                     newViewDate = new Date(this.viewDate);
21219                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21220                 }
21221                 if (this.dateWithinRange(newDate)){
21222                     this.date = newDate;
21223                     this.viewDate = newViewDate;
21224                     this.setValue(this.formatDate(this.date));
21225 //                    this.update();
21226                     e.preventDefault();
21227                     dateChanged = true;
21228                 }
21229                 break;
21230             case 38: // up
21231             case 40: // down
21232                 if (!this.keyboardNavigation) {
21233                     break;
21234                 }
21235                 dir = e.keyCode == 38 ? -1 : 1;
21236                 if (e.ctrlKey){
21237                     newDate = this.moveYear(this.date, dir);
21238                     newViewDate = this.moveYear(this.viewDate, dir);
21239                 } else if (e.shiftKey){
21240                     newDate = this.moveMonth(this.date, dir);
21241                     newViewDate = this.moveMonth(this.viewDate, dir);
21242                 } else {
21243                     newDate = new Date(this.date);
21244                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21245                     newViewDate = new Date(this.viewDate);
21246                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21247                 }
21248                 if (this.dateWithinRange(newDate)){
21249                     this.date = newDate;
21250                     this.viewDate = newViewDate;
21251                     this.setValue(this.formatDate(this.date));
21252 //                    this.update();
21253                     e.preventDefault();
21254                     dateChanged = true;
21255                 }
21256                 break;
21257             case 13: // enter
21258                 this.setValue(this.formatDate(this.date));
21259                 this.hidePopup();
21260                 e.preventDefault();
21261                 break;
21262             case 9: // tab
21263                 this.setValue(this.formatDate(this.date));
21264                 this.hidePopup();
21265                 break;
21266             case 16: // shift
21267             case 17: // ctrl
21268             case 18: // alt
21269                 break;
21270             default :
21271                 this.hidePopup();
21272                 
21273         }
21274     },
21275     
21276     
21277     onClick: function(e) 
21278     {
21279         e.stopPropagation();
21280         e.preventDefault();
21281         
21282         var target = e.getTarget();
21283         
21284         if(target.nodeName.toLowerCase() === 'i'){
21285             target = Roo.get(target).dom.parentNode;
21286         }
21287         
21288         var nodeName = target.nodeName;
21289         var className = target.className;
21290         var html = target.innerHTML;
21291         //Roo.log(nodeName);
21292         
21293         switch(nodeName.toLowerCase()) {
21294             case 'th':
21295                 switch(className) {
21296                     case 'switch':
21297                         this.showMode(1);
21298                         break;
21299                     case 'prev':
21300                     case 'next':
21301                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21302                         switch(this.viewMode){
21303                                 case 0:
21304                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21305                                         break;
21306                                 case 1:
21307                                 case 2:
21308                                         this.viewDate = this.moveYear(this.viewDate, dir);
21309                                         break;
21310                         }
21311                         this.fill();
21312                         break;
21313                     case 'today':
21314                         var date = new Date();
21315                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21316 //                        this.fill()
21317                         this.setValue(this.formatDate(this.date));
21318                         
21319                         this.hidePopup();
21320                         break;
21321                 }
21322                 break;
21323             case 'span':
21324                 if (className.indexOf('disabled') < 0) {
21325                     this.viewDate.setUTCDate(1);
21326                     if (className.indexOf('month') > -1) {
21327                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21328                     } else {
21329                         var year = parseInt(html, 10) || 0;
21330                         this.viewDate.setUTCFullYear(year);
21331                         
21332                     }
21333                     
21334                     if(this.singleMode){
21335                         this.setValue(this.formatDate(this.viewDate));
21336                         this.hidePopup();
21337                         return;
21338                     }
21339                     
21340                     this.showMode(-1);
21341                     this.fill();
21342                 }
21343                 break;
21344                 
21345             case 'td':
21346                 //Roo.log(className);
21347                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21348                     var day = parseInt(html, 10) || 1;
21349                     var year = this.viewDate.getUTCFullYear(),
21350                         month = this.viewDate.getUTCMonth();
21351
21352                     if (className.indexOf('old') > -1) {
21353                         if(month === 0 ){
21354                             month = 11;
21355                             year -= 1;
21356                         }else{
21357                             month -= 1;
21358                         }
21359                     } else if (className.indexOf('new') > -1) {
21360                         if (month == 11) {
21361                             month = 0;
21362                             year += 1;
21363                         } else {
21364                             month += 1;
21365                         }
21366                     }
21367                     //Roo.log([year,month,day]);
21368                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21369                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21370 //                    this.fill();
21371                     //Roo.log(this.formatDate(this.date));
21372                     this.setValue(this.formatDate(this.date));
21373                     this.hidePopup();
21374                 }
21375                 break;
21376         }
21377     },
21378     
21379     setStartDate: function(startDate)
21380     {
21381         this.startDate = startDate || -Infinity;
21382         if (this.startDate !== -Infinity) {
21383             this.startDate = this.parseDate(this.startDate);
21384         }
21385         this.update();
21386         this.updateNavArrows();
21387     },
21388
21389     setEndDate: function(endDate)
21390     {
21391         this.endDate = endDate || Infinity;
21392         if (this.endDate !== Infinity) {
21393             this.endDate = this.parseDate(this.endDate);
21394         }
21395         this.update();
21396         this.updateNavArrows();
21397     },
21398     
21399     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21400     {
21401         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21402         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21403             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21404         }
21405         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21406             return parseInt(d, 10);
21407         });
21408         this.update();
21409         this.updateNavArrows();
21410     },
21411     
21412     updateNavArrows: function() 
21413     {
21414         if(this.singleMode){
21415             return;
21416         }
21417         
21418         var d = new Date(this.viewDate),
21419         year = d.getUTCFullYear(),
21420         month = d.getUTCMonth();
21421         
21422         Roo.each(this.picker().select('.prev', true).elements, function(v){
21423             v.show();
21424             switch (this.viewMode) {
21425                 case 0:
21426
21427                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21428                         v.hide();
21429                     }
21430                     break;
21431                 case 1:
21432                 case 2:
21433                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21434                         v.hide();
21435                     }
21436                     break;
21437             }
21438         });
21439         
21440         Roo.each(this.picker().select('.next', true).elements, function(v){
21441             v.show();
21442             switch (this.viewMode) {
21443                 case 0:
21444
21445                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21446                         v.hide();
21447                     }
21448                     break;
21449                 case 1:
21450                 case 2:
21451                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21452                         v.hide();
21453                     }
21454                     break;
21455             }
21456         })
21457     },
21458     
21459     moveMonth: function(date, dir)
21460     {
21461         if (!dir) {
21462             return date;
21463         }
21464         var new_date = new Date(date.valueOf()),
21465         day = new_date.getUTCDate(),
21466         month = new_date.getUTCMonth(),
21467         mag = Math.abs(dir),
21468         new_month, test;
21469         dir = dir > 0 ? 1 : -1;
21470         if (mag == 1){
21471             test = dir == -1
21472             // If going back one month, make sure month is not current month
21473             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21474             ? function(){
21475                 return new_date.getUTCMonth() == month;
21476             }
21477             // If going forward one month, make sure month is as expected
21478             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21479             : function(){
21480                 return new_date.getUTCMonth() != new_month;
21481             };
21482             new_month = month + dir;
21483             new_date.setUTCMonth(new_month);
21484             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21485             if (new_month < 0 || new_month > 11) {
21486                 new_month = (new_month + 12) % 12;
21487             }
21488         } else {
21489             // For magnitudes >1, move one month at a time...
21490             for (var i=0; i<mag; i++) {
21491                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21492                 new_date = this.moveMonth(new_date, dir);
21493             }
21494             // ...then reset the day, keeping it in the new month
21495             new_month = new_date.getUTCMonth();
21496             new_date.setUTCDate(day);
21497             test = function(){
21498                 return new_month != new_date.getUTCMonth();
21499             };
21500         }
21501         // Common date-resetting loop -- if date is beyond end of month, make it
21502         // end of month
21503         while (test()){
21504             new_date.setUTCDate(--day);
21505             new_date.setUTCMonth(new_month);
21506         }
21507         return new_date;
21508     },
21509
21510     moveYear: function(date, dir)
21511     {
21512         return this.moveMonth(date, dir*12);
21513     },
21514
21515     dateWithinRange: function(date)
21516     {
21517         return date >= this.startDate && date <= this.endDate;
21518     },
21519
21520     
21521     remove: function() 
21522     {
21523         this.picker().remove();
21524     },
21525     
21526     validateValue : function(value)
21527     {
21528         if(this.getVisibilityEl().hasClass('hidden')){
21529             return true;
21530         }
21531         
21532         if(value.length < 1)  {
21533             if(this.allowBlank){
21534                 return true;
21535             }
21536             return false;
21537         }
21538         
21539         if(value.length < this.minLength){
21540             return false;
21541         }
21542         if(value.length > this.maxLength){
21543             return false;
21544         }
21545         if(this.vtype){
21546             var vt = Roo.form.VTypes;
21547             if(!vt[this.vtype](value, this)){
21548                 return false;
21549             }
21550         }
21551         if(typeof this.validator == "function"){
21552             var msg = this.validator(value);
21553             if(msg !== true){
21554                 return false;
21555             }
21556         }
21557         
21558         if(this.regex && !this.regex.test(value)){
21559             return false;
21560         }
21561         
21562         if(typeof(this.parseDate(value)) == 'undefined'){
21563             return false;
21564         }
21565         
21566         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21567             return false;
21568         }      
21569         
21570         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21571             return false;
21572         } 
21573         
21574         
21575         return true;
21576     },
21577     
21578     reset : function()
21579     {
21580         this.date = this.viewDate = '';
21581         
21582         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21583     }
21584    
21585 });
21586
21587 Roo.apply(Roo.bootstrap.DateField,  {
21588     
21589     head : {
21590         tag: 'thead',
21591         cn: [
21592         {
21593             tag: 'tr',
21594             cn: [
21595             {
21596                 tag: 'th',
21597                 cls: 'prev',
21598                 html: '<i class="fa fa-arrow-left"/>'
21599             },
21600             {
21601                 tag: 'th',
21602                 cls: 'switch',
21603                 colspan: '5'
21604             },
21605             {
21606                 tag: 'th',
21607                 cls: 'next',
21608                 html: '<i class="fa fa-arrow-right"/>'
21609             }
21610
21611             ]
21612         }
21613         ]
21614     },
21615     
21616     content : {
21617         tag: 'tbody',
21618         cn: [
21619         {
21620             tag: 'tr',
21621             cn: [
21622             {
21623                 tag: 'td',
21624                 colspan: '7'
21625             }
21626             ]
21627         }
21628         ]
21629     },
21630     
21631     footer : {
21632         tag: 'tfoot',
21633         cn: [
21634         {
21635             tag: 'tr',
21636             cn: [
21637             {
21638                 tag: 'th',
21639                 colspan: '7',
21640                 cls: 'today'
21641             }
21642                     
21643             ]
21644         }
21645         ]
21646     },
21647     
21648     dates:{
21649         en: {
21650             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21651             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21652             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21653             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21654             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21655             today: "Today"
21656         }
21657     },
21658     
21659     modes: [
21660     {
21661         clsName: 'days',
21662         navFnc: 'Month',
21663         navStep: 1
21664     },
21665     {
21666         clsName: 'months',
21667         navFnc: 'FullYear',
21668         navStep: 1
21669     },
21670     {
21671         clsName: 'years',
21672         navFnc: 'FullYear',
21673         navStep: 10
21674     }]
21675 });
21676
21677 Roo.apply(Roo.bootstrap.DateField,  {
21678   
21679     template : {
21680         tag: 'div',
21681         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21682         cn: [
21683         {
21684             tag: 'div',
21685             cls: 'datepicker-days',
21686             cn: [
21687             {
21688                 tag: 'table',
21689                 cls: 'table-condensed',
21690                 cn:[
21691                 Roo.bootstrap.DateField.head,
21692                 {
21693                     tag: 'tbody'
21694                 },
21695                 Roo.bootstrap.DateField.footer
21696                 ]
21697             }
21698             ]
21699         },
21700         {
21701             tag: 'div',
21702             cls: 'datepicker-months',
21703             cn: [
21704             {
21705                 tag: 'table',
21706                 cls: 'table-condensed',
21707                 cn:[
21708                 Roo.bootstrap.DateField.head,
21709                 Roo.bootstrap.DateField.content,
21710                 Roo.bootstrap.DateField.footer
21711                 ]
21712             }
21713             ]
21714         },
21715         {
21716             tag: 'div',
21717             cls: 'datepicker-years',
21718             cn: [
21719             {
21720                 tag: 'table',
21721                 cls: 'table-condensed',
21722                 cn:[
21723                 Roo.bootstrap.DateField.head,
21724                 Roo.bootstrap.DateField.content,
21725                 Roo.bootstrap.DateField.footer
21726                 ]
21727             }
21728             ]
21729         }
21730         ]
21731     }
21732 });
21733
21734  
21735
21736  /*
21737  * - LGPL
21738  *
21739  * TimeField
21740  * 
21741  */
21742
21743 /**
21744  * @class Roo.bootstrap.TimeField
21745  * @extends Roo.bootstrap.Input
21746  * Bootstrap DateField class
21747  * 
21748  * 
21749  * @constructor
21750  * Create a new TimeField
21751  * @param {Object} config The config object
21752  */
21753
21754 Roo.bootstrap.TimeField = function(config){
21755     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21756     this.addEvents({
21757             /**
21758              * @event show
21759              * Fires when this field show.
21760              * @param {Roo.bootstrap.DateField} thisthis
21761              * @param {Mixed} date The date value
21762              */
21763             show : true,
21764             /**
21765              * @event show
21766              * Fires when this field hide.
21767              * @param {Roo.bootstrap.DateField} this
21768              * @param {Mixed} date The date value
21769              */
21770             hide : true,
21771             /**
21772              * @event select
21773              * Fires when select a date.
21774              * @param {Roo.bootstrap.DateField} this
21775              * @param {Mixed} date The date value
21776              */
21777             select : true
21778         });
21779 };
21780
21781 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21782     
21783     /**
21784      * @cfg {String} format
21785      * The default time format string which can be overriden for localization support.  The format must be
21786      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21787      */
21788     format : "H:i",
21789        
21790     onRender: function(ct, position)
21791     {
21792         
21793         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21794                 
21795         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21796         
21797         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21798         
21799         this.pop = this.picker().select('>.datepicker-time',true).first();
21800         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21801         
21802         this.picker().on('mousedown', this.onMousedown, this);
21803         this.picker().on('click', this.onClick, this);
21804         
21805         this.picker().addClass('datepicker-dropdown');
21806     
21807         this.fillTime();
21808         this.update();
21809             
21810         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21811         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21812         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21813         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21814         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21815         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21816
21817     },
21818     
21819     fireKey: function(e){
21820         if (!this.picker().isVisible()){
21821             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21822                 this.show();
21823             }
21824             return;
21825         }
21826
21827         e.preventDefault();
21828         
21829         switch(e.keyCode){
21830             case 27: // escape
21831                 this.hide();
21832                 break;
21833             case 37: // left
21834             case 39: // right
21835                 this.onTogglePeriod();
21836                 break;
21837             case 38: // up
21838                 this.onIncrementMinutes();
21839                 break;
21840             case 40: // down
21841                 this.onDecrementMinutes();
21842                 break;
21843             case 13: // enter
21844             case 9: // tab
21845                 this.setTime();
21846                 break;
21847         }
21848     },
21849     
21850     onClick: function(e) {
21851         e.stopPropagation();
21852         e.preventDefault();
21853     },
21854     
21855     picker : function()
21856     {
21857         return this.el.select('.datepicker', true).first();
21858     },
21859     
21860     fillTime: function()
21861     {    
21862         var time = this.pop.select('tbody', true).first();
21863         
21864         time.dom.innerHTML = '';
21865         
21866         time.createChild({
21867             tag: 'tr',
21868             cn: [
21869                 {
21870                     tag: 'td',
21871                     cn: [
21872                         {
21873                             tag: 'a',
21874                             href: '#',
21875                             cls: 'btn',
21876                             cn: [
21877                                 {
21878                                     tag: 'span',
21879                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21880                                 }
21881                             ]
21882                         } 
21883                     ]
21884                 },
21885                 {
21886                     tag: 'td',
21887                     cls: 'separator'
21888                 },
21889                 {
21890                     tag: 'td',
21891                     cn: [
21892                         {
21893                             tag: 'a',
21894                             href: '#',
21895                             cls: 'btn',
21896                             cn: [
21897                                 {
21898                                     tag: 'span',
21899                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21900                                 }
21901                             ]
21902                         }
21903                     ]
21904                 },
21905                 {
21906                     tag: 'td',
21907                     cls: 'separator'
21908                 }
21909             ]
21910         });
21911         
21912         time.createChild({
21913             tag: 'tr',
21914             cn: [
21915                 {
21916                     tag: 'td',
21917                     cn: [
21918                         {
21919                             tag: 'span',
21920                             cls: 'timepicker-hour',
21921                             html: '00'
21922                         }  
21923                     ]
21924                 },
21925                 {
21926                     tag: 'td',
21927                     cls: 'separator',
21928                     html: ':'
21929                 },
21930                 {
21931                     tag: 'td',
21932                     cn: [
21933                         {
21934                             tag: 'span',
21935                             cls: 'timepicker-minute',
21936                             html: '00'
21937                         }  
21938                     ]
21939                 },
21940                 {
21941                     tag: 'td',
21942                     cls: 'separator'
21943                 },
21944                 {
21945                     tag: 'td',
21946                     cn: [
21947                         {
21948                             tag: 'button',
21949                             type: 'button',
21950                             cls: 'btn btn-primary period',
21951                             html: 'AM'
21952                             
21953                         }
21954                     ]
21955                 }
21956             ]
21957         });
21958         
21959         time.createChild({
21960             tag: 'tr',
21961             cn: [
21962                 {
21963                     tag: 'td',
21964                     cn: [
21965                         {
21966                             tag: 'a',
21967                             href: '#',
21968                             cls: 'btn',
21969                             cn: [
21970                                 {
21971                                     tag: 'span',
21972                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21973                                 }
21974                             ]
21975                         }
21976                     ]
21977                 },
21978                 {
21979                     tag: 'td',
21980                     cls: 'separator'
21981                 },
21982                 {
21983                     tag: 'td',
21984                     cn: [
21985                         {
21986                             tag: 'a',
21987                             href: '#',
21988                             cls: 'btn',
21989                             cn: [
21990                                 {
21991                                     tag: 'span',
21992                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21993                                 }
21994                             ]
21995                         }
21996                     ]
21997                 },
21998                 {
21999                     tag: 'td',
22000                     cls: 'separator'
22001                 }
22002             ]
22003         });
22004         
22005     },
22006     
22007     update: function()
22008     {
22009         
22010         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22011         
22012         this.fill();
22013     },
22014     
22015     fill: function() 
22016     {
22017         var hours = this.time.getHours();
22018         var minutes = this.time.getMinutes();
22019         var period = 'AM';
22020         
22021         if(hours > 11){
22022             period = 'PM';
22023         }
22024         
22025         if(hours == 0){
22026             hours = 12;
22027         }
22028         
22029         
22030         if(hours > 12){
22031             hours = hours - 12;
22032         }
22033         
22034         if(hours < 10){
22035             hours = '0' + hours;
22036         }
22037         
22038         if(minutes < 10){
22039             minutes = '0' + minutes;
22040         }
22041         
22042         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22043         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22044         this.pop.select('button', true).first().dom.innerHTML = period;
22045         
22046     },
22047     
22048     place: function()
22049     {   
22050         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22051         
22052         var cls = ['bottom'];
22053         
22054         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22055             cls.pop();
22056             cls.push('top');
22057         }
22058         
22059         cls.push('right');
22060         
22061         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22062             cls.pop();
22063             cls.push('left');
22064         }
22065         
22066         this.picker().addClass(cls.join('-'));
22067         
22068         var _this = this;
22069         
22070         Roo.each(cls, function(c){
22071             if(c == 'bottom'){
22072                 _this.picker().setTop(_this.inputEl().getHeight());
22073                 return;
22074             }
22075             if(c == 'top'){
22076                 _this.picker().setTop(0 - _this.picker().getHeight());
22077                 return;
22078             }
22079             
22080             if(c == 'left'){
22081                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22082                 return;
22083             }
22084             if(c == 'right'){
22085                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22086                 return;
22087             }
22088         });
22089         
22090     },
22091   
22092     onFocus : function()
22093     {
22094         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22095         this.show();
22096     },
22097     
22098     onBlur : function()
22099     {
22100         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22101         this.hide();
22102     },
22103     
22104     show : function()
22105     {
22106         this.picker().show();
22107         this.pop.show();
22108         this.update();
22109         this.place();
22110         
22111         this.fireEvent('show', this, this.date);
22112     },
22113     
22114     hide : function()
22115     {
22116         this.picker().hide();
22117         this.pop.hide();
22118         
22119         this.fireEvent('hide', this, this.date);
22120     },
22121     
22122     setTime : function()
22123     {
22124         this.hide();
22125         this.setValue(this.time.format(this.format));
22126         
22127         this.fireEvent('select', this, this.date);
22128         
22129         
22130     },
22131     
22132     onMousedown: function(e){
22133         e.stopPropagation();
22134         e.preventDefault();
22135     },
22136     
22137     onIncrementHours: function()
22138     {
22139         Roo.log('onIncrementHours');
22140         this.time = this.time.add(Date.HOUR, 1);
22141         this.update();
22142         
22143     },
22144     
22145     onDecrementHours: function()
22146     {
22147         Roo.log('onDecrementHours');
22148         this.time = this.time.add(Date.HOUR, -1);
22149         this.update();
22150     },
22151     
22152     onIncrementMinutes: function()
22153     {
22154         Roo.log('onIncrementMinutes');
22155         this.time = this.time.add(Date.MINUTE, 1);
22156         this.update();
22157     },
22158     
22159     onDecrementMinutes: function()
22160     {
22161         Roo.log('onDecrementMinutes');
22162         this.time = this.time.add(Date.MINUTE, -1);
22163         this.update();
22164     },
22165     
22166     onTogglePeriod: function()
22167     {
22168         Roo.log('onTogglePeriod');
22169         this.time = this.time.add(Date.HOUR, 12);
22170         this.update();
22171     }
22172     
22173    
22174 });
22175
22176 Roo.apply(Roo.bootstrap.TimeField,  {
22177     
22178     content : {
22179         tag: 'tbody',
22180         cn: [
22181             {
22182                 tag: 'tr',
22183                 cn: [
22184                 {
22185                     tag: 'td',
22186                     colspan: '7'
22187                 }
22188                 ]
22189             }
22190         ]
22191     },
22192     
22193     footer : {
22194         tag: 'tfoot',
22195         cn: [
22196             {
22197                 tag: 'tr',
22198                 cn: [
22199                 {
22200                     tag: 'th',
22201                     colspan: '7',
22202                     cls: '',
22203                     cn: [
22204                         {
22205                             tag: 'button',
22206                             cls: 'btn btn-info ok',
22207                             html: 'OK'
22208                         }
22209                     ]
22210                 }
22211
22212                 ]
22213             }
22214         ]
22215     }
22216 });
22217
22218 Roo.apply(Roo.bootstrap.TimeField,  {
22219   
22220     template : {
22221         tag: 'div',
22222         cls: 'datepicker dropdown-menu',
22223         cn: [
22224             {
22225                 tag: 'div',
22226                 cls: 'datepicker-time',
22227                 cn: [
22228                 {
22229                     tag: 'table',
22230                     cls: 'table-condensed',
22231                     cn:[
22232                     Roo.bootstrap.TimeField.content,
22233                     Roo.bootstrap.TimeField.footer
22234                     ]
22235                 }
22236                 ]
22237             }
22238         ]
22239     }
22240 });
22241
22242  
22243
22244  /*
22245  * - LGPL
22246  *
22247  * MonthField
22248  * 
22249  */
22250
22251 /**
22252  * @class Roo.bootstrap.MonthField
22253  * @extends Roo.bootstrap.Input
22254  * Bootstrap MonthField class
22255  * 
22256  * @cfg {String} language default en
22257  * 
22258  * @constructor
22259  * Create a new MonthField
22260  * @param {Object} config The config object
22261  */
22262
22263 Roo.bootstrap.MonthField = function(config){
22264     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22265     
22266     this.addEvents({
22267         /**
22268          * @event show
22269          * Fires when this field show.
22270          * @param {Roo.bootstrap.MonthField} this
22271          * @param {Mixed} date The date value
22272          */
22273         show : true,
22274         /**
22275          * @event show
22276          * Fires when this field hide.
22277          * @param {Roo.bootstrap.MonthField} this
22278          * @param {Mixed} date The date value
22279          */
22280         hide : true,
22281         /**
22282          * @event select
22283          * Fires when select a date.
22284          * @param {Roo.bootstrap.MonthField} this
22285          * @param {String} oldvalue The old value
22286          * @param {String} newvalue The new value
22287          */
22288         select : true
22289     });
22290 };
22291
22292 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22293     
22294     onRender: function(ct, position)
22295     {
22296         
22297         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22298         
22299         this.language = this.language || 'en';
22300         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22301         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22302         
22303         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22304         this.isInline = false;
22305         this.isInput = true;
22306         this.component = this.el.select('.add-on', true).first() || false;
22307         this.component = (this.component && this.component.length === 0) ? false : this.component;
22308         this.hasInput = this.component && this.inputEL().length;
22309         
22310         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22311         
22312         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22313         
22314         this.picker().on('mousedown', this.onMousedown, this);
22315         this.picker().on('click', this.onClick, this);
22316         
22317         this.picker().addClass('datepicker-dropdown');
22318         
22319         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22320             v.setStyle('width', '189px');
22321         });
22322         
22323         this.fillMonths();
22324         
22325         this.update();
22326         
22327         if(this.isInline) {
22328             this.show();
22329         }
22330         
22331     },
22332     
22333     setValue: function(v, suppressEvent)
22334     {   
22335         var o = this.getValue();
22336         
22337         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22338         
22339         this.update();
22340
22341         if(suppressEvent !== true){
22342             this.fireEvent('select', this, o, v);
22343         }
22344         
22345     },
22346     
22347     getValue: function()
22348     {
22349         return this.value;
22350     },
22351     
22352     onClick: function(e) 
22353     {
22354         e.stopPropagation();
22355         e.preventDefault();
22356         
22357         var target = e.getTarget();
22358         
22359         if(target.nodeName.toLowerCase() === 'i'){
22360             target = Roo.get(target).dom.parentNode;
22361         }
22362         
22363         var nodeName = target.nodeName;
22364         var className = target.className;
22365         var html = target.innerHTML;
22366         
22367         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22368             return;
22369         }
22370         
22371         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22372         
22373         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22374         
22375         this.hide();
22376                         
22377     },
22378     
22379     picker : function()
22380     {
22381         return this.pickerEl;
22382     },
22383     
22384     fillMonths: function()
22385     {    
22386         var i = 0;
22387         var months = this.picker().select('>.datepicker-months td', true).first();
22388         
22389         months.dom.innerHTML = '';
22390         
22391         while (i < 12) {
22392             var month = {
22393                 tag: 'span',
22394                 cls: 'month',
22395                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22396             };
22397             
22398             months.createChild(month);
22399         }
22400         
22401     },
22402     
22403     update: function()
22404     {
22405         var _this = this;
22406         
22407         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22408             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22409         }
22410         
22411         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22412             e.removeClass('active');
22413             
22414             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22415                 e.addClass('active');
22416             }
22417         })
22418     },
22419     
22420     place: function()
22421     {
22422         if(this.isInline) {
22423             return;
22424         }
22425         
22426         this.picker().removeClass(['bottom', 'top']);
22427         
22428         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22429             /*
22430              * place to the top of element!
22431              *
22432              */
22433             
22434             this.picker().addClass('top');
22435             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22436             
22437             return;
22438         }
22439         
22440         this.picker().addClass('bottom');
22441         
22442         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22443     },
22444     
22445     onFocus : function()
22446     {
22447         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22448         this.show();
22449     },
22450     
22451     onBlur : function()
22452     {
22453         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22454         
22455         var d = this.inputEl().getValue();
22456         
22457         this.setValue(d);
22458                 
22459         this.hide();
22460     },
22461     
22462     show : function()
22463     {
22464         this.picker().show();
22465         this.picker().select('>.datepicker-months', true).first().show();
22466         this.update();
22467         this.place();
22468         
22469         this.fireEvent('show', this, this.date);
22470     },
22471     
22472     hide : function()
22473     {
22474         if(this.isInline) {
22475             return;
22476         }
22477         this.picker().hide();
22478         this.fireEvent('hide', this, this.date);
22479         
22480     },
22481     
22482     onMousedown: function(e)
22483     {
22484         e.stopPropagation();
22485         e.preventDefault();
22486     },
22487     
22488     keyup: function(e)
22489     {
22490         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22491         this.update();
22492     },
22493
22494     fireKey: function(e)
22495     {
22496         if (!this.picker().isVisible()){
22497             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22498                 this.show();
22499             }
22500             return;
22501         }
22502         
22503         var dir;
22504         
22505         switch(e.keyCode){
22506             case 27: // escape
22507                 this.hide();
22508                 e.preventDefault();
22509                 break;
22510             case 37: // left
22511             case 39: // right
22512                 dir = e.keyCode == 37 ? -1 : 1;
22513                 
22514                 this.vIndex = this.vIndex + dir;
22515                 
22516                 if(this.vIndex < 0){
22517                     this.vIndex = 0;
22518                 }
22519                 
22520                 if(this.vIndex > 11){
22521                     this.vIndex = 11;
22522                 }
22523                 
22524                 if(isNaN(this.vIndex)){
22525                     this.vIndex = 0;
22526                 }
22527                 
22528                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22529                 
22530                 break;
22531             case 38: // up
22532             case 40: // down
22533                 
22534                 dir = e.keyCode == 38 ? -1 : 1;
22535                 
22536                 this.vIndex = this.vIndex + dir * 4;
22537                 
22538                 if(this.vIndex < 0){
22539                     this.vIndex = 0;
22540                 }
22541                 
22542                 if(this.vIndex > 11){
22543                     this.vIndex = 11;
22544                 }
22545                 
22546                 if(isNaN(this.vIndex)){
22547                     this.vIndex = 0;
22548                 }
22549                 
22550                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22551                 break;
22552                 
22553             case 13: // enter
22554                 
22555                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22556                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22557                 }
22558                 
22559                 this.hide();
22560                 e.preventDefault();
22561                 break;
22562             case 9: // tab
22563                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22564                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22565                 }
22566                 this.hide();
22567                 break;
22568             case 16: // shift
22569             case 17: // ctrl
22570             case 18: // alt
22571                 break;
22572             default :
22573                 this.hide();
22574                 
22575         }
22576     },
22577     
22578     remove: function() 
22579     {
22580         this.picker().remove();
22581     }
22582    
22583 });
22584
22585 Roo.apply(Roo.bootstrap.MonthField,  {
22586     
22587     content : {
22588         tag: 'tbody',
22589         cn: [
22590         {
22591             tag: 'tr',
22592             cn: [
22593             {
22594                 tag: 'td',
22595                 colspan: '7'
22596             }
22597             ]
22598         }
22599         ]
22600     },
22601     
22602     dates:{
22603         en: {
22604             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22605             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22606         }
22607     }
22608 });
22609
22610 Roo.apply(Roo.bootstrap.MonthField,  {
22611   
22612     template : {
22613         tag: 'div',
22614         cls: 'datepicker dropdown-menu roo-dynamic',
22615         cn: [
22616             {
22617                 tag: 'div',
22618                 cls: 'datepicker-months',
22619                 cn: [
22620                 {
22621                     tag: 'table',
22622                     cls: 'table-condensed',
22623                     cn:[
22624                         Roo.bootstrap.DateField.content
22625                     ]
22626                 }
22627                 ]
22628             }
22629         ]
22630     }
22631 });
22632
22633  
22634
22635  
22636  /*
22637  * - LGPL
22638  *
22639  * CheckBox
22640  * 
22641  */
22642
22643 /**
22644  * @class Roo.bootstrap.CheckBox
22645  * @extends Roo.bootstrap.Input
22646  * Bootstrap CheckBox class
22647  * 
22648  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22649  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22650  * @cfg {String} boxLabel The text that appears beside the checkbox
22651  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22652  * @cfg {Boolean} checked initnal the element
22653  * @cfg {Boolean} inline inline the element (default false)
22654  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22655  * @cfg {String} tooltip label tooltip
22656  * 
22657  * @constructor
22658  * Create a new CheckBox
22659  * @param {Object} config The config object
22660  */
22661
22662 Roo.bootstrap.CheckBox = function(config){
22663     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22664    
22665     this.addEvents({
22666         /**
22667         * @event check
22668         * Fires when the element is checked or unchecked.
22669         * @param {Roo.bootstrap.CheckBox} this This input
22670         * @param {Boolean} checked The new checked value
22671         */
22672        check : true,
22673        /**
22674         * @event click
22675         * Fires when the element is click.
22676         * @param {Roo.bootstrap.CheckBox} this This input
22677         */
22678        click : true
22679     });
22680     
22681 };
22682
22683 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22684   
22685     inputType: 'checkbox',
22686     inputValue: 1,
22687     valueOff: 0,
22688     boxLabel: false,
22689     checked: false,
22690     weight : false,
22691     inline: false,
22692     tooltip : '',
22693     
22694     // checkbox success does not make any sense really.. 
22695     invalidClass : "",
22696     validClass : "",
22697     
22698     
22699     getAutoCreate : function()
22700     {
22701         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22702         
22703         var id = Roo.id();
22704         
22705         var cfg = {};
22706         
22707         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22708         
22709         if(this.inline){
22710             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22711         }
22712         
22713         var input =  {
22714             tag: 'input',
22715             id : id,
22716             type : this.inputType,
22717             value : this.inputValue,
22718             cls : 'roo-' + this.inputType, //'form-box',
22719             placeholder : this.placeholder || ''
22720             
22721         };
22722         
22723         if(this.inputType != 'radio'){
22724             var hidden =  {
22725                 tag: 'input',
22726                 type : 'hidden',
22727                 cls : 'roo-hidden-value',
22728                 value : this.checked ? this.inputValue : this.valueOff
22729             };
22730         }
22731         
22732             
22733         if (this.weight) { // Validity check?
22734             cfg.cls += " " + this.inputType + "-" + this.weight;
22735         }
22736         
22737         if (this.disabled) {
22738             input.disabled=true;
22739         }
22740         
22741         if(this.checked){
22742             input.checked = this.checked;
22743         }
22744         
22745         if (this.name) {
22746             
22747             input.name = this.name;
22748             
22749             if(this.inputType != 'radio'){
22750                 hidden.name = this.name;
22751                 input.name = '_hidden_' + this.name;
22752             }
22753         }
22754         
22755         if (this.size) {
22756             input.cls += ' input-' + this.size;
22757         }
22758         
22759         var settings=this;
22760         
22761         ['xs','sm','md','lg'].map(function(size){
22762             if (settings[size]) {
22763                 cfg.cls += ' col-' + size + '-' + settings[size];
22764             }
22765         });
22766         
22767         var inputblock = input;
22768          
22769         if (this.before || this.after) {
22770             
22771             inputblock = {
22772                 cls : 'input-group',
22773                 cn :  [] 
22774             };
22775             
22776             if (this.before) {
22777                 inputblock.cn.push({
22778                     tag :'span',
22779                     cls : 'input-group-addon',
22780                     html : this.before
22781                 });
22782             }
22783             
22784             inputblock.cn.push(input);
22785             
22786             if(this.inputType != 'radio'){
22787                 inputblock.cn.push(hidden);
22788             }
22789             
22790             if (this.after) {
22791                 inputblock.cn.push({
22792                     tag :'span',
22793                     cls : 'input-group-addon',
22794                     html : this.after
22795                 });
22796             }
22797             
22798         }
22799         var boxLabelCfg = false;
22800         
22801         if(this.boxLabel){
22802            
22803             boxLabelCfg = {
22804                 tag: 'label',
22805                 //'for': id, // box label is handled by onclick - so no for...
22806                 cls: 'box-label',
22807                 html: this.boxLabel
22808             };
22809             if(this.tooltip){
22810                 boxLabelCfg.tooltip = this.tooltip;
22811             }
22812              
22813         }
22814         
22815         
22816         if (align ==='left' && this.fieldLabel.length) {
22817 //                Roo.log("left and has label");
22818             cfg.cn = [
22819                 {
22820                     tag: 'label',
22821                     'for' :  id,
22822                     cls : 'control-label',
22823                     html : this.fieldLabel
22824                 },
22825                 {
22826                     cls : "", 
22827                     cn: [
22828                         inputblock
22829                     ]
22830                 }
22831             ];
22832             
22833             if (boxLabelCfg) {
22834                 cfg.cn[1].cn.push(boxLabelCfg);
22835             }
22836             
22837             if(this.labelWidth > 12){
22838                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22839             }
22840             
22841             if(this.labelWidth < 13 && this.labelmd == 0){
22842                 this.labelmd = this.labelWidth;
22843             }
22844             
22845             if(this.labellg > 0){
22846                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22847                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22848             }
22849             
22850             if(this.labelmd > 0){
22851                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22852                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22853             }
22854             
22855             if(this.labelsm > 0){
22856                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22857                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22858             }
22859             
22860             if(this.labelxs > 0){
22861                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22862                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22863             }
22864             
22865         } else if ( this.fieldLabel.length) {
22866 //                Roo.log(" label");
22867                 cfg.cn = [
22868                    
22869                     {
22870                         tag: this.boxLabel ? 'span' : 'label',
22871                         'for': id,
22872                         cls: 'control-label box-input-label',
22873                         //cls : 'input-group-addon',
22874                         html : this.fieldLabel
22875                     },
22876                     
22877                     inputblock
22878                     
22879                 ];
22880                 if (boxLabelCfg) {
22881                     cfg.cn.push(boxLabelCfg);
22882                 }
22883
22884         } else {
22885             
22886 //                Roo.log(" no label && no align");
22887                 cfg.cn = [  inputblock ] ;
22888                 if (boxLabelCfg) {
22889                     cfg.cn.push(boxLabelCfg);
22890                 }
22891
22892                 
22893         }
22894         
22895        
22896         
22897         if(this.inputType != 'radio'){
22898             cfg.cn.push(hidden);
22899         }
22900         
22901         return cfg;
22902         
22903     },
22904     
22905     /**
22906      * return the real input element.
22907      */
22908     inputEl: function ()
22909     {
22910         return this.el.select('input.roo-' + this.inputType,true).first();
22911     },
22912     hiddenEl: function ()
22913     {
22914         return this.el.select('input.roo-hidden-value',true).first();
22915     },
22916     
22917     labelEl: function()
22918     {
22919         return this.el.select('label.control-label',true).first();
22920     },
22921     /* depricated... */
22922     
22923     label: function()
22924     {
22925         return this.labelEl();
22926     },
22927     
22928     boxLabelEl: function()
22929     {
22930         return this.el.select('label.box-label',true).first();
22931     },
22932     
22933     initEvents : function()
22934     {
22935 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22936         
22937         this.inputEl().on('click', this.onClick,  this);
22938         
22939         if (this.boxLabel) { 
22940             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22941         }
22942         
22943         this.startValue = this.getValue();
22944         
22945         if(this.groupId){
22946             Roo.bootstrap.CheckBox.register(this);
22947         }
22948     },
22949     
22950     onClick : function(e)
22951     {   
22952         if(this.fireEvent('click', this, e) !== false){
22953             this.setChecked(!this.checked);
22954         }
22955         
22956     },
22957     
22958     setChecked : function(state,suppressEvent)
22959     {
22960         this.startValue = this.getValue();
22961
22962         if(this.inputType == 'radio'){
22963             
22964             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22965                 e.dom.checked = false;
22966             });
22967             
22968             this.inputEl().dom.checked = true;
22969             
22970             this.inputEl().dom.value = this.inputValue;
22971             
22972             if(suppressEvent !== true){
22973                 this.fireEvent('check', this, true);
22974             }
22975             
22976             this.validate();
22977             
22978             return;
22979         }
22980         
22981         this.checked = state;
22982         
22983         this.inputEl().dom.checked = state;
22984         
22985         
22986         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22987         
22988         if(suppressEvent !== true){
22989             this.fireEvent('check', this, state);
22990         }
22991         
22992         this.validate();
22993     },
22994     
22995     getValue : function()
22996     {
22997         if(this.inputType == 'radio'){
22998             return this.getGroupValue();
22999         }
23000         
23001         return this.hiddenEl().dom.value;
23002         
23003     },
23004     
23005     getGroupValue : function()
23006     {
23007         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23008             return '';
23009         }
23010         
23011         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23012     },
23013     
23014     setValue : function(v,suppressEvent)
23015     {
23016         if(this.inputType == 'radio'){
23017             this.setGroupValue(v, suppressEvent);
23018             return;
23019         }
23020         
23021         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23022         
23023         this.validate();
23024     },
23025     
23026     setGroupValue : function(v, suppressEvent)
23027     {
23028         this.startValue = this.getValue();
23029         
23030         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23031             e.dom.checked = false;
23032             
23033             if(e.dom.value == v){
23034                 e.dom.checked = true;
23035             }
23036         });
23037         
23038         if(suppressEvent !== true){
23039             this.fireEvent('check', this, true);
23040         }
23041
23042         this.validate();
23043         
23044         return;
23045     },
23046     
23047     validate : function()
23048     {
23049         if(this.getVisibilityEl().hasClass('hidden')){
23050             return true;
23051         }
23052         
23053         if(
23054                 this.disabled || 
23055                 (this.inputType == 'radio' && this.validateRadio()) ||
23056                 (this.inputType == 'checkbox' && this.validateCheckbox())
23057         ){
23058             this.markValid();
23059             return true;
23060         }
23061         
23062         this.markInvalid();
23063         return false;
23064     },
23065     
23066     validateRadio : function()
23067     {
23068         if(this.getVisibilityEl().hasClass('hidden')){
23069             return true;
23070         }
23071         
23072         if(this.allowBlank){
23073             return true;
23074         }
23075         
23076         var valid = false;
23077         
23078         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23079             if(!e.dom.checked){
23080                 return;
23081             }
23082             
23083             valid = true;
23084             
23085             return false;
23086         });
23087         
23088         return valid;
23089     },
23090     
23091     validateCheckbox : function()
23092     {
23093         if(!this.groupId){
23094             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23095             //return (this.getValue() == this.inputValue) ? true : false;
23096         }
23097         
23098         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23099         
23100         if(!group){
23101             return false;
23102         }
23103         
23104         var r = false;
23105         
23106         for(var i in group){
23107             if(group[i].el.isVisible(true)){
23108                 r = false;
23109                 break;
23110             }
23111             
23112             r = true;
23113         }
23114         
23115         for(var i in group){
23116             if(r){
23117                 break;
23118             }
23119             
23120             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23121         }
23122         
23123         return r;
23124     },
23125     
23126     /**
23127      * Mark this field as valid
23128      */
23129     markValid : function()
23130     {
23131         var _this = this;
23132         
23133         this.fireEvent('valid', this);
23134         
23135         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23136         
23137         if(this.groupId){
23138             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23139         }
23140         
23141         if(label){
23142             label.markValid();
23143         }
23144
23145         if(this.inputType == 'radio'){
23146             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23147                 var fg = e.findParent('.form-group', false, true);
23148                 if (Roo.bootstrap.version == 3) {
23149                     fg.removeClass([_this.invalidClass, _this.validClass]);
23150                     fg.addClass(_this.validClass);
23151                 } else {
23152                     fg.removeClass(['is-valid', 'is-invalid']);
23153                     fg.addClass('is-valid');
23154                 }
23155             });
23156             
23157             return;
23158         }
23159
23160         if(!this.groupId){
23161             var fg = this.el.findParent('.form-group', false, true);
23162             if (Roo.bootstrap.version == 3) {
23163                 fg.removeClass([this.invalidClass, this.validClass]);
23164                 fg.addClass(this.validClass);
23165             } else {
23166                 fg.removeClass(['is-valid', 'is-invalid']);
23167                 fg.addClass('is-valid');
23168             }
23169             return;
23170         }
23171         
23172         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23173         
23174         if(!group){
23175             return;
23176         }
23177         
23178         for(var i in group){
23179             var fg = group[i].el.findParent('.form-group', false, true);
23180             if (Roo.bootstrap.version == 3) {
23181                 fg.removeClass([this.invalidClass, this.validClass]);
23182                 fg.addClass(this.validClass);
23183             } else {
23184                 fg.removeClass(['is-valid', 'is-invalid']);
23185                 fg.addClass('is-valid');
23186             }
23187         }
23188     },
23189     
23190      /**
23191      * Mark this field as invalid
23192      * @param {String} msg The validation message
23193      */
23194     markInvalid : function(msg)
23195     {
23196         if(this.allowBlank){
23197             return;
23198         }
23199         
23200         var _this = this;
23201         
23202         this.fireEvent('invalid', this, msg);
23203         
23204         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23205         
23206         if(this.groupId){
23207             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23208         }
23209         
23210         if(label){
23211             label.markInvalid();
23212         }
23213             
23214         if(this.inputType == 'radio'){
23215             
23216             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23217                 var fg = e.findParent('.form-group', false, true);
23218                 if (Roo.bootstrap.version == 3) {
23219                     fg.removeClass([_this.invalidClass, _this.validClass]);
23220                     fg.addClass(_this.invalidClass);
23221                 } else {
23222                     fg.removeClass(['is-invalid', 'is-valid']);
23223                     fg.addClass('is-invalid');
23224                 }
23225             });
23226             
23227             return;
23228         }
23229         
23230         if(!this.groupId){
23231             var fg = this.el.findParent('.form-group', false, true);
23232             if (Roo.bootstrap.version == 3) {
23233                 fg.removeClass([_this.invalidClass, _this.validClass]);
23234                 fg.addClass(_this.invalidClass);
23235             } else {
23236                 fg.removeClass(['is-invalid', 'is-valid']);
23237                 fg.addClass('is-invalid');
23238             }
23239             return;
23240         }
23241         
23242         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23243         
23244         if(!group){
23245             return;
23246         }
23247         
23248         for(var i in group){
23249             var fg = group[i].el.findParent('.form-group', false, true);
23250             if (Roo.bootstrap.version == 3) {
23251                 fg.removeClass([_this.invalidClass, _this.validClass]);
23252                 fg.addClass(_this.invalidClass);
23253             } else {
23254                 fg.removeClass(['is-invalid', 'is-valid']);
23255                 fg.addClass('is-invalid');
23256             }
23257         }
23258         
23259     },
23260     
23261     clearInvalid : function()
23262     {
23263         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23264         
23265         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23266         
23267         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23268         
23269         if (label && label.iconEl) {
23270             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23271             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23272         }
23273     },
23274     
23275     disable : function()
23276     {
23277         if(this.inputType != 'radio'){
23278             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23279             return;
23280         }
23281         
23282         var _this = this;
23283         
23284         if(this.rendered){
23285             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23286                 _this.getActionEl().addClass(this.disabledClass);
23287                 e.dom.disabled = true;
23288             });
23289         }
23290         
23291         this.disabled = true;
23292         this.fireEvent("disable", this);
23293         return this;
23294     },
23295
23296     enable : function()
23297     {
23298         if(this.inputType != 'radio'){
23299             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23300             return;
23301         }
23302         
23303         var _this = this;
23304         
23305         if(this.rendered){
23306             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23307                 _this.getActionEl().removeClass(this.disabledClass);
23308                 e.dom.disabled = false;
23309             });
23310         }
23311         
23312         this.disabled = false;
23313         this.fireEvent("enable", this);
23314         return this;
23315     },
23316     
23317     setBoxLabel : function(v)
23318     {
23319         this.boxLabel = v;
23320         
23321         if(this.rendered){
23322             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23323         }
23324     }
23325
23326 });
23327
23328 Roo.apply(Roo.bootstrap.CheckBox, {
23329     
23330     groups: {},
23331     
23332      /**
23333     * register a CheckBox Group
23334     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23335     */
23336     register : function(checkbox)
23337     {
23338         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23339             this.groups[checkbox.groupId] = {};
23340         }
23341         
23342         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23343             return;
23344         }
23345         
23346         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23347         
23348     },
23349     /**
23350     * fetch a CheckBox Group based on the group ID
23351     * @param {string} the group ID
23352     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23353     */
23354     get: function(groupId) {
23355         if (typeof(this.groups[groupId]) == 'undefined') {
23356             return false;
23357         }
23358         
23359         return this.groups[groupId] ;
23360     }
23361     
23362     
23363 });
23364 /*
23365  * - LGPL
23366  *
23367  * RadioItem
23368  * 
23369  */
23370
23371 /**
23372  * @class Roo.bootstrap.Radio
23373  * @extends Roo.bootstrap.Component
23374  * Bootstrap Radio class
23375  * @cfg {String} boxLabel - the label associated
23376  * @cfg {String} value - the value of radio
23377  * 
23378  * @constructor
23379  * Create a new Radio
23380  * @param {Object} config The config object
23381  */
23382 Roo.bootstrap.Radio = function(config){
23383     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23384     
23385 };
23386
23387 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23388     
23389     boxLabel : '',
23390     
23391     value : '',
23392     
23393     getAutoCreate : function()
23394     {
23395         var cfg = {
23396             tag : 'div',
23397             cls : 'form-group radio',
23398             cn : [
23399                 {
23400                     tag : 'label',
23401                     cls : 'box-label',
23402                     html : this.boxLabel
23403                 }
23404             ]
23405         };
23406         
23407         return cfg;
23408     },
23409     
23410     initEvents : function() 
23411     {
23412         this.parent().register(this);
23413         
23414         this.el.on('click', this.onClick, this);
23415         
23416     },
23417     
23418     onClick : function(e)
23419     {
23420         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23421             this.setChecked(true);
23422         }
23423     },
23424     
23425     setChecked : function(state, suppressEvent)
23426     {
23427         this.parent().setValue(this.value, suppressEvent);
23428         
23429     },
23430     
23431     setBoxLabel : function(v)
23432     {
23433         this.boxLabel = v;
23434         
23435         if(this.rendered){
23436             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23437         }
23438     }
23439     
23440 });
23441  
23442
23443  /*
23444  * - LGPL
23445  *
23446  * Input
23447  * 
23448  */
23449
23450 /**
23451  * @class Roo.bootstrap.SecurePass
23452  * @extends Roo.bootstrap.Input
23453  * Bootstrap SecurePass class
23454  *
23455  * 
23456  * @constructor
23457  * Create a new SecurePass
23458  * @param {Object} config The config object
23459  */
23460  
23461 Roo.bootstrap.SecurePass = function (config) {
23462     // these go here, so the translation tool can replace them..
23463     this.errors = {
23464         PwdEmpty: "Please type a password, and then retype it to confirm.",
23465         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23466         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23467         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23468         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23469         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23470         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23471         TooWeak: "Your password is Too Weak."
23472     },
23473     this.meterLabel = "Password strength:";
23474     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23475     this.meterClass = [
23476         "roo-password-meter-tooweak", 
23477         "roo-password-meter-weak", 
23478         "roo-password-meter-medium", 
23479         "roo-password-meter-strong", 
23480         "roo-password-meter-grey"
23481     ];
23482     
23483     this.errors = {};
23484     
23485     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23486 }
23487
23488 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23489     /**
23490      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23491      * {
23492      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23493      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23494      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23495      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23496      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23497      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23498      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23499      * })
23500      */
23501     // private
23502     
23503     meterWidth: 300,
23504     errorMsg :'',    
23505     errors: false,
23506     imageRoot: '/',
23507     /**
23508      * @cfg {String/Object} Label for the strength meter (defaults to
23509      * 'Password strength:')
23510      */
23511     // private
23512     meterLabel: '',
23513     /**
23514      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23515      * ['Weak', 'Medium', 'Strong'])
23516      */
23517     // private    
23518     pwdStrengths: false,    
23519     // private
23520     strength: 0,
23521     // private
23522     _lastPwd: null,
23523     // private
23524     kCapitalLetter: 0,
23525     kSmallLetter: 1,
23526     kDigit: 2,
23527     kPunctuation: 3,
23528     
23529     insecure: false,
23530     // private
23531     initEvents: function ()
23532     {
23533         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23534
23535         if (this.el.is('input[type=password]') && Roo.isSafari) {
23536             this.el.on('keydown', this.SafariOnKeyDown, this);
23537         }
23538
23539         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23540     },
23541     // private
23542     onRender: function (ct, position)
23543     {
23544         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23545         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23546         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23547
23548         this.trigger.createChild({
23549                    cn: [
23550                     {
23551                     //id: 'PwdMeter',
23552                     tag: 'div',
23553                     cls: 'roo-password-meter-grey col-xs-12',
23554                     style: {
23555                         //width: 0,
23556                         //width: this.meterWidth + 'px'                                                
23557                         }
23558                     },
23559                     {                            
23560                          cls: 'roo-password-meter-text'                          
23561                     }
23562                 ]            
23563         });
23564
23565          
23566         if (this.hideTrigger) {
23567             this.trigger.setDisplayed(false);
23568         }
23569         this.setSize(this.width || '', this.height || '');
23570     },
23571     // private
23572     onDestroy: function ()
23573     {
23574         if (this.trigger) {
23575             this.trigger.removeAllListeners();
23576             this.trigger.remove();
23577         }
23578         if (this.wrap) {
23579             this.wrap.remove();
23580         }
23581         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23582     },
23583     // private
23584     checkStrength: function ()
23585     {
23586         var pwd = this.inputEl().getValue();
23587         if (pwd == this._lastPwd) {
23588             return;
23589         }
23590
23591         var strength;
23592         if (this.ClientSideStrongPassword(pwd)) {
23593             strength = 3;
23594         } else if (this.ClientSideMediumPassword(pwd)) {
23595             strength = 2;
23596         } else if (this.ClientSideWeakPassword(pwd)) {
23597             strength = 1;
23598         } else {
23599             strength = 0;
23600         }
23601         
23602         Roo.log('strength1: ' + strength);
23603         
23604         //var pm = this.trigger.child('div/div/div').dom;
23605         var pm = this.trigger.child('div/div');
23606         pm.removeClass(this.meterClass);
23607         pm.addClass(this.meterClass[strength]);
23608                 
23609         
23610         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23611                 
23612         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23613         
23614         this._lastPwd = pwd;
23615     },
23616     reset: function ()
23617     {
23618         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23619         
23620         this._lastPwd = '';
23621         
23622         var pm = this.trigger.child('div/div');
23623         pm.removeClass(this.meterClass);
23624         pm.addClass('roo-password-meter-grey');        
23625         
23626         
23627         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23628         
23629         pt.innerHTML = '';
23630         this.inputEl().dom.type='password';
23631     },
23632     // private
23633     validateValue: function (value)
23634     {
23635         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23636             return false;
23637         }
23638         if (value.length == 0) {
23639             if (this.allowBlank) {
23640                 this.clearInvalid();
23641                 return true;
23642             }
23643
23644             this.markInvalid(this.errors.PwdEmpty);
23645             this.errorMsg = this.errors.PwdEmpty;
23646             return false;
23647         }
23648         
23649         if(this.insecure){
23650             return true;
23651         }
23652         
23653         if (!value.match(/[\x21-\x7e]+/)) {
23654             this.markInvalid(this.errors.PwdBadChar);
23655             this.errorMsg = this.errors.PwdBadChar;
23656             return false;
23657         }
23658         if (value.length < 6) {
23659             this.markInvalid(this.errors.PwdShort);
23660             this.errorMsg = this.errors.PwdShort;
23661             return false;
23662         }
23663         if (value.length > 16) {
23664             this.markInvalid(this.errors.PwdLong);
23665             this.errorMsg = this.errors.PwdLong;
23666             return false;
23667         }
23668         var strength;
23669         if (this.ClientSideStrongPassword(value)) {
23670             strength = 3;
23671         } else if (this.ClientSideMediumPassword(value)) {
23672             strength = 2;
23673         } else if (this.ClientSideWeakPassword(value)) {
23674             strength = 1;
23675         } else {
23676             strength = 0;
23677         }
23678
23679         
23680         if (strength < 2) {
23681             //this.markInvalid(this.errors.TooWeak);
23682             this.errorMsg = this.errors.TooWeak;
23683             //return false;
23684         }
23685         
23686         
23687         console.log('strength2: ' + strength);
23688         
23689         //var pm = this.trigger.child('div/div/div').dom;
23690         
23691         var pm = this.trigger.child('div/div');
23692         pm.removeClass(this.meterClass);
23693         pm.addClass(this.meterClass[strength]);
23694                 
23695         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23696                 
23697         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23698         
23699         this.errorMsg = ''; 
23700         return true;
23701     },
23702     // private
23703     CharacterSetChecks: function (type)
23704     {
23705         this.type = type;
23706         this.fResult = false;
23707     },
23708     // private
23709     isctype: function (character, type)
23710     {
23711         switch (type) {  
23712             case this.kCapitalLetter:
23713                 if (character >= 'A' && character <= 'Z') {
23714                     return true;
23715                 }
23716                 break;
23717             
23718             case this.kSmallLetter:
23719                 if (character >= 'a' && character <= 'z') {
23720                     return true;
23721                 }
23722                 break;
23723             
23724             case this.kDigit:
23725                 if (character >= '0' && character <= '9') {
23726                     return true;
23727                 }
23728                 break;
23729             
23730             case this.kPunctuation:
23731                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23732                     return true;
23733                 }
23734                 break;
23735             
23736             default:
23737                 return false;
23738         }
23739
23740     },
23741     // private
23742     IsLongEnough: function (pwd, size)
23743     {
23744         return !(pwd == null || isNaN(size) || pwd.length < size);
23745     },
23746     // private
23747     SpansEnoughCharacterSets: function (word, nb)
23748     {
23749         if (!this.IsLongEnough(word, nb))
23750         {
23751             return false;
23752         }
23753
23754         var characterSetChecks = new Array(
23755             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23756             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23757         );
23758         
23759         for (var index = 0; index < word.length; ++index) {
23760             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23761                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23762                     characterSetChecks[nCharSet].fResult = true;
23763                     break;
23764                 }
23765             }
23766         }
23767
23768         var nCharSets = 0;
23769         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23770             if (characterSetChecks[nCharSet].fResult) {
23771                 ++nCharSets;
23772             }
23773         }
23774
23775         if (nCharSets < nb) {
23776             return false;
23777         }
23778         return true;
23779     },
23780     // private
23781     ClientSideStrongPassword: function (pwd)
23782     {
23783         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23784     },
23785     // private
23786     ClientSideMediumPassword: function (pwd)
23787     {
23788         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23789     },
23790     // private
23791     ClientSideWeakPassword: function (pwd)
23792     {
23793         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23794     }
23795           
23796 })//<script type="text/javascript">
23797
23798 /*
23799  * Based  Ext JS Library 1.1.1
23800  * Copyright(c) 2006-2007, Ext JS, LLC.
23801  * LGPL
23802  *
23803  */
23804  
23805 /**
23806  * @class Roo.HtmlEditorCore
23807  * @extends Roo.Component
23808  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23809  *
23810  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23811  */
23812
23813 Roo.HtmlEditorCore = function(config){
23814     
23815     
23816     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23817     
23818     
23819     this.addEvents({
23820         /**
23821          * @event initialize
23822          * Fires when the editor is fully initialized (including the iframe)
23823          * @param {Roo.HtmlEditorCore} this
23824          */
23825         initialize: true,
23826         /**
23827          * @event activate
23828          * Fires when the editor is first receives the focus. Any insertion must wait
23829          * until after this event.
23830          * @param {Roo.HtmlEditorCore} this
23831          */
23832         activate: true,
23833          /**
23834          * @event beforesync
23835          * Fires before the textarea is updated with content from the editor iframe. Return false
23836          * to cancel the sync.
23837          * @param {Roo.HtmlEditorCore} this
23838          * @param {String} html
23839          */
23840         beforesync: true,
23841          /**
23842          * @event beforepush
23843          * Fires before the iframe editor is updated with content from the textarea. Return false
23844          * to cancel the push.
23845          * @param {Roo.HtmlEditorCore} this
23846          * @param {String} html
23847          */
23848         beforepush: true,
23849          /**
23850          * @event sync
23851          * Fires when the textarea is updated with content from the editor iframe.
23852          * @param {Roo.HtmlEditorCore} this
23853          * @param {String} html
23854          */
23855         sync: true,
23856          /**
23857          * @event push
23858          * Fires when the iframe editor is updated with content from the textarea.
23859          * @param {Roo.HtmlEditorCore} this
23860          * @param {String} html
23861          */
23862         push: true,
23863         
23864         /**
23865          * @event editorevent
23866          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23867          * @param {Roo.HtmlEditorCore} this
23868          */
23869         editorevent: true
23870         
23871     });
23872     
23873     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23874     
23875     // defaults : white / black...
23876     this.applyBlacklists();
23877     
23878     
23879     
23880 };
23881
23882
23883 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23884
23885
23886      /**
23887      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23888      */
23889     
23890     owner : false,
23891     
23892      /**
23893      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23894      *                        Roo.resizable.
23895      */
23896     resizable : false,
23897      /**
23898      * @cfg {Number} height (in pixels)
23899      */   
23900     height: 300,
23901    /**
23902      * @cfg {Number} width (in pixels)
23903      */   
23904     width: 500,
23905     
23906     /**
23907      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23908      * 
23909      */
23910     stylesheets: false,
23911     
23912     // id of frame..
23913     frameId: false,
23914     
23915     // private properties
23916     validationEvent : false,
23917     deferHeight: true,
23918     initialized : false,
23919     activated : false,
23920     sourceEditMode : false,
23921     onFocus : Roo.emptyFn,
23922     iframePad:3,
23923     hideMode:'offsets',
23924     
23925     clearUp: true,
23926     
23927     // blacklist + whitelisted elements..
23928     black: false,
23929     white: false,
23930      
23931     bodyCls : '',
23932
23933     /**
23934      * Protected method that will not generally be called directly. It
23935      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23936      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23937      */
23938     getDocMarkup : function(){
23939         // body styles..
23940         var st = '';
23941         
23942         // inherit styels from page...?? 
23943         if (this.stylesheets === false) {
23944             
23945             Roo.get(document.head).select('style').each(function(node) {
23946                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23947             });
23948             
23949             Roo.get(document.head).select('link').each(function(node) { 
23950                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23951             });
23952             
23953         } else if (!this.stylesheets.length) {
23954                 // simple..
23955                 st = '<style type="text/css">' +
23956                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23957                    '</style>';
23958         } else {
23959             for (var i in this.stylesheets) { 
23960                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23961             }
23962             
23963         }
23964         
23965         st +=  '<style type="text/css">' +
23966             'IMG { cursor: pointer } ' +
23967         '</style>';
23968
23969         var cls = 'roo-htmleditor-body';
23970         
23971         if(this.bodyCls.length){
23972             cls += ' ' + this.bodyCls;
23973         }
23974         
23975         return '<html><head>' + st  +
23976             //<style type="text/css">' +
23977             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23978             //'</style>' +
23979             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23980     },
23981
23982     // private
23983     onRender : function(ct, position)
23984     {
23985         var _t = this;
23986         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23987         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23988         
23989         
23990         this.el.dom.style.border = '0 none';
23991         this.el.dom.setAttribute('tabIndex', -1);
23992         this.el.addClass('x-hidden hide');
23993         
23994         
23995         
23996         if(Roo.isIE){ // fix IE 1px bogus margin
23997             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23998         }
23999        
24000         
24001         this.frameId = Roo.id();
24002         
24003          
24004         
24005         var iframe = this.owner.wrap.createChild({
24006             tag: 'iframe',
24007             cls: 'form-control', // bootstrap..
24008             id: this.frameId,
24009             name: this.frameId,
24010             frameBorder : 'no',
24011             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24012         }, this.el
24013         );
24014         
24015         
24016         this.iframe = iframe.dom;
24017
24018          this.assignDocWin();
24019         
24020         this.doc.designMode = 'on';
24021        
24022         this.doc.open();
24023         this.doc.write(this.getDocMarkup());
24024         this.doc.close();
24025
24026         
24027         var task = { // must defer to wait for browser to be ready
24028             run : function(){
24029                 //console.log("run task?" + this.doc.readyState);
24030                 this.assignDocWin();
24031                 if(this.doc.body || this.doc.readyState == 'complete'){
24032                     try {
24033                         this.doc.designMode="on";
24034                     } catch (e) {
24035                         return;
24036                     }
24037                     Roo.TaskMgr.stop(task);
24038                     this.initEditor.defer(10, this);
24039                 }
24040             },
24041             interval : 10,
24042             duration: 10000,
24043             scope: this
24044         };
24045         Roo.TaskMgr.start(task);
24046
24047     },
24048
24049     // private
24050     onResize : function(w, h)
24051     {
24052          Roo.log('resize: ' +w + ',' + h );
24053         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24054         if(!this.iframe){
24055             return;
24056         }
24057         if(typeof w == 'number'){
24058             
24059             this.iframe.style.width = w + 'px';
24060         }
24061         if(typeof h == 'number'){
24062             
24063             this.iframe.style.height = h + 'px';
24064             if(this.doc){
24065                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24066             }
24067         }
24068         
24069     },
24070
24071     /**
24072      * Toggles the editor between standard and source edit mode.
24073      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24074      */
24075     toggleSourceEdit : function(sourceEditMode){
24076         
24077         this.sourceEditMode = sourceEditMode === true;
24078         
24079         if(this.sourceEditMode){
24080  
24081             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24082             
24083         }else{
24084             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24085             //this.iframe.className = '';
24086             this.deferFocus();
24087         }
24088         //this.setSize(this.owner.wrap.getSize());
24089         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24090     },
24091
24092     
24093   
24094
24095     /**
24096      * Protected method that will not generally be called directly. If you need/want
24097      * custom HTML cleanup, this is the method you should override.
24098      * @param {String} html The HTML to be cleaned
24099      * return {String} The cleaned HTML
24100      */
24101     cleanHtml : function(html){
24102         html = String(html);
24103         if(html.length > 5){
24104             if(Roo.isSafari){ // strip safari nonsense
24105                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24106             }
24107         }
24108         if(html == '&nbsp;'){
24109             html = '';
24110         }
24111         return html;
24112     },
24113
24114     /**
24115      * HTML Editor -> Textarea
24116      * Protected method that will not generally be called directly. Syncs the contents
24117      * of the editor iframe with the textarea.
24118      */
24119     syncValue : function(){
24120         if(this.initialized){
24121             var bd = (this.doc.body || this.doc.documentElement);
24122             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24123             var html = bd.innerHTML;
24124             if(Roo.isSafari){
24125                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24126                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24127                 if(m && m[1]){
24128                     html = '<div style="'+m[0]+'">' + html + '</div>';
24129                 }
24130             }
24131             html = this.cleanHtml(html);
24132             // fix up the special chars.. normaly like back quotes in word...
24133             // however we do not want to do this with chinese..
24134             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24135                 
24136                 var cc = match.charCodeAt();
24137
24138                 // Get the character value, handling surrogate pairs
24139                 if (match.length == 2) {
24140                     // It's a surrogate pair, calculate the Unicode code point
24141                     var high = match.charCodeAt(0) - 0xD800;
24142                     var low  = match.charCodeAt(1) - 0xDC00;
24143                     cc = (high * 0x400) + low + 0x10000;
24144                 }  else if (
24145                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24146                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24147                     (cc >= 0xf900 && cc < 0xfb00 )
24148                 ) {
24149                         return match;
24150                 }  
24151          
24152                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24153                 return "&#" + cc + ";";
24154                 
24155                 
24156             });
24157             
24158             
24159              
24160             if(this.owner.fireEvent('beforesync', this, html) !== false){
24161                 this.el.dom.value = html;
24162                 this.owner.fireEvent('sync', this, html);
24163             }
24164         }
24165     },
24166
24167     /**
24168      * Protected method that will not generally be called directly. Pushes the value of the textarea
24169      * into the iframe editor.
24170      */
24171     pushValue : function(){
24172         if(this.initialized){
24173             var v = this.el.dom.value.trim();
24174             
24175 //            if(v.length < 1){
24176 //                v = '&#160;';
24177 //            }
24178             
24179             if(this.owner.fireEvent('beforepush', this, v) !== false){
24180                 var d = (this.doc.body || this.doc.documentElement);
24181                 d.innerHTML = v;
24182                 this.cleanUpPaste();
24183                 this.el.dom.value = d.innerHTML;
24184                 this.owner.fireEvent('push', this, v);
24185             }
24186         }
24187     },
24188
24189     // private
24190     deferFocus : function(){
24191         this.focus.defer(10, this);
24192     },
24193
24194     // doc'ed in Field
24195     focus : function(){
24196         if(this.win && !this.sourceEditMode){
24197             this.win.focus();
24198         }else{
24199             this.el.focus();
24200         }
24201     },
24202     
24203     assignDocWin: function()
24204     {
24205         var iframe = this.iframe;
24206         
24207          if(Roo.isIE){
24208             this.doc = iframe.contentWindow.document;
24209             this.win = iframe.contentWindow;
24210         } else {
24211 //            if (!Roo.get(this.frameId)) {
24212 //                return;
24213 //            }
24214 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24215 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24216             
24217             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24218                 return;
24219             }
24220             
24221             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24222             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24223         }
24224     },
24225     
24226     // private
24227     initEditor : function(){
24228         //console.log("INIT EDITOR");
24229         this.assignDocWin();
24230         
24231         
24232         
24233         this.doc.designMode="on";
24234         this.doc.open();
24235         this.doc.write(this.getDocMarkup());
24236         this.doc.close();
24237         
24238         var dbody = (this.doc.body || this.doc.documentElement);
24239         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24240         // this copies styles from the containing element into thsi one..
24241         // not sure why we need all of this..
24242         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24243         
24244         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24245         //ss['background-attachment'] = 'fixed'; // w3c
24246         dbody.bgProperties = 'fixed'; // ie
24247         //Roo.DomHelper.applyStyles(dbody, ss);
24248         Roo.EventManager.on(this.doc, {
24249             //'mousedown': this.onEditorEvent,
24250             'mouseup': this.onEditorEvent,
24251             'dblclick': this.onEditorEvent,
24252             'click': this.onEditorEvent,
24253             'keyup': this.onEditorEvent,
24254             buffer:100,
24255             scope: this
24256         });
24257         if(Roo.isGecko){
24258             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24259         }
24260         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24261             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24262         }
24263         this.initialized = true;
24264
24265         this.owner.fireEvent('initialize', this);
24266         this.pushValue();
24267     },
24268
24269     // private
24270     onDestroy : function(){
24271         
24272         
24273         
24274         if(this.rendered){
24275             
24276             //for (var i =0; i < this.toolbars.length;i++) {
24277             //    // fixme - ask toolbars for heights?
24278             //    this.toolbars[i].onDestroy();
24279            // }
24280             
24281             //this.wrap.dom.innerHTML = '';
24282             //this.wrap.remove();
24283         }
24284     },
24285
24286     // private
24287     onFirstFocus : function(){
24288         
24289         this.assignDocWin();
24290         
24291         
24292         this.activated = true;
24293          
24294     
24295         if(Roo.isGecko){ // prevent silly gecko errors
24296             this.win.focus();
24297             var s = this.win.getSelection();
24298             if(!s.focusNode || s.focusNode.nodeType != 3){
24299                 var r = s.getRangeAt(0);
24300                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24301                 r.collapse(true);
24302                 this.deferFocus();
24303             }
24304             try{
24305                 this.execCmd('useCSS', true);
24306                 this.execCmd('styleWithCSS', false);
24307             }catch(e){}
24308         }
24309         this.owner.fireEvent('activate', this);
24310     },
24311
24312     // private
24313     adjustFont: function(btn){
24314         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24315         //if(Roo.isSafari){ // safari
24316         //    adjust *= 2;
24317        // }
24318         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24319         if(Roo.isSafari){ // safari
24320             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24321             v =  (v < 10) ? 10 : v;
24322             v =  (v > 48) ? 48 : v;
24323             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24324             
24325         }
24326         
24327         
24328         v = Math.max(1, v+adjust);
24329         
24330         this.execCmd('FontSize', v  );
24331     },
24332
24333     onEditorEvent : function(e)
24334     {
24335         this.owner.fireEvent('editorevent', this, e);
24336       //  this.updateToolbar();
24337         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24338     },
24339
24340     insertTag : function(tg)
24341     {
24342         // could be a bit smarter... -> wrap the current selected tRoo..
24343         if (tg.toLowerCase() == 'span' ||
24344             tg.toLowerCase() == 'code' ||
24345             tg.toLowerCase() == 'sup' ||
24346             tg.toLowerCase() == 'sub' 
24347             ) {
24348             
24349             range = this.createRange(this.getSelection());
24350             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24351             wrappingNode.appendChild(range.extractContents());
24352             range.insertNode(wrappingNode);
24353
24354             return;
24355             
24356             
24357             
24358         }
24359         this.execCmd("formatblock",   tg);
24360         
24361     },
24362     
24363     insertText : function(txt)
24364     {
24365         
24366         
24367         var range = this.createRange();
24368         range.deleteContents();
24369                //alert(Sender.getAttribute('label'));
24370                
24371         range.insertNode(this.doc.createTextNode(txt));
24372     } ,
24373     
24374      
24375
24376     /**
24377      * Executes a Midas editor command on the editor document and performs necessary focus and
24378      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24379      * @param {String} cmd The Midas command
24380      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24381      */
24382     relayCmd : function(cmd, value){
24383         this.win.focus();
24384         this.execCmd(cmd, value);
24385         this.owner.fireEvent('editorevent', this);
24386         //this.updateToolbar();
24387         this.owner.deferFocus();
24388     },
24389
24390     /**
24391      * Executes a Midas editor command directly on the editor document.
24392      * For visual commands, you should use {@link #relayCmd} instead.
24393      * <b>This should only be called after the editor is initialized.</b>
24394      * @param {String} cmd The Midas command
24395      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24396      */
24397     execCmd : function(cmd, value){
24398         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24399         this.syncValue();
24400     },
24401  
24402  
24403    
24404     /**
24405      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24406      * to insert tRoo.
24407      * @param {String} text | dom node.. 
24408      */
24409     insertAtCursor : function(text)
24410     {
24411         
24412         if(!this.activated){
24413             return;
24414         }
24415         /*
24416         if(Roo.isIE){
24417             this.win.focus();
24418             var r = this.doc.selection.createRange();
24419             if(r){
24420                 r.collapse(true);
24421                 r.pasteHTML(text);
24422                 this.syncValue();
24423                 this.deferFocus();
24424             
24425             }
24426             return;
24427         }
24428         */
24429         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24430             this.win.focus();
24431             
24432             
24433             // from jquery ui (MIT licenced)
24434             var range, node;
24435             var win = this.win;
24436             
24437             if (win.getSelection && win.getSelection().getRangeAt) {
24438                 range = win.getSelection().getRangeAt(0);
24439                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24440                 range.insertNode(node);
24441             } else if (win.document.selection && win.document.selection.createRange) {
24442                 // no firefox support
24443                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24444                 win.document.selection.createRange().pasteHTML(txt);
24445             } else {
24446                 // no firefox support
24447                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24448                 this.execCmd('InsertHTML', txt);
24449             } 
24450             
24451             this.syncValue();
24452             
24453             this.deferFocus();
24454         }
24455     },
24456  // private
24457     mozKeyPress : function(e){
24458         if(e.ctrlKey){
24459             var c = e.getCharCode(), cmd;
24460           
24461             if(c > 0){
24462                 c = String.fromCharCode(c).toLowerCase();
24463                 switch(c){
24464                     case 'b':
24465                         cmd = 'bold';
24466                         break;
24467                     case 'i':
24468                         cmd = 'italic';
24469                         break;
24470                     
24471                     case 'u':
24472                         cmd = 'underline';
24473                         break;
24474                     
24475                     case 'v':
24476                         this.cleanUpPaste.defer(100, this);
24477                         return;
24478                         
24479                 }
24480                 if(cmd){
24481                     this.win.focus();
24482                     this.execCmd(cmd);
24483                     this.deferFocus();
24484                     e.preventDefault();
24485                 }
24486                 
24487             }
24488         }
24489     },
24490
24491     // private
24492     fixKeys : function(){ // load time branching for fastest keydown performance
24493         if(Roo.isIE){
24494             return function(e){
24495                 var k = e.getKey(), r;
24496                 if(k == e.TAB){
24497                     e.stopEvent();
24498                     r = this.doc.selection.createRange();
24499                     if(r){
24500                         r.collapse(true);
24501                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24502                         this.deferFocus();
24503                     }
24504                     return;
24505                 }
24506                 
24507                 if(k == e.ENTER){
24508                     r = this.doc.selection.createRange();
24509                     if(r){
24510                         var target = r.parentElement();
24511                         if(!target || target.tagName.toLowerCase() != 'li'){
24512                             e.stopEvent();
24513                             r.pasteHTML('<br />');
24514                             r.collapse(false);
24515                             r.select();
24516                         }
24517                     }
24518                 }
24519                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24520                     this.cleanUpPaste.defer(100, this);
24521                     return;
24522                 }
24523                 
24524                 
24525             };
24526         }else if(Roo.isOpera){
24527             return function(e){
24528                 var k = e.getKey();
24529                 if(k == e.TAB){
24530                     e.stopEvent();
24531                     this.win.focus();
24532                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24533                     this.deferFocus();
24534                 }
24535                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24536                     this.cleanUpPaste.defer(100, this);
24537                     return;
24538                 }
24539                 
24540             };
24541         }else if(Roo.isSafari){
24542             return function(e){
24543                 var k = e.getKey();
24544                 
24545                 if(k == e.TAB){
24546                     e.stopEvent();
24547                     this.execCmd('InsertText','\t');
24548                     this.deferFocus();
24549                     return;
24550                 }
24551                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24552                     this.cleanUpPaste.defer(100, this);
24553                     return;
24554                 }
24555                 
24556              };
24557         }
24558     }(),
24559     
24560     getAllAncestors: function()
24561     {
24562         var p = this.getSelectedNode();
24563         var a = [];
24564         if (!p) {
24565             a.push(p); // push blank onto stack..
24566             p = this.getParentElement();
24567         }
24568         
24569         
24570         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24571             a.push(p);
24572             p = p.parentNode;
24573         }
24574         a.push(this.doc.body);
24575         return a;
24576     },
24577     lastSel : false,
24578     lastSelNode : false,
24579     
24580     
24581     getSelection : function() 
24582     {
24583         this.assignDocWin();
24584         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24585     },
24586     
24587     getSelectedNode: function() 
24588     {
24589         // this may only work on Gecko!!!
24590         
24591         // should we cache this!!!!
24592         
24593         
24594         
24595          
24596         var range = this.createRange(this.getSelection()).cloneRange();
24597         
24598         if (Roo.isIE) {
24599             var parent = range.parentElement();
24600             while (true) {
24601                 var testRange = range.duplicate();
24602                 testRange.moveToElementText(parent);
24603                 if (testRange.inRange(range)) {
24604                     break;
24605                 }
24606                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24607                     break;
24608                 }
24609                 parent = parent.parentElement;
24610             }
24611             return parent;
24612         }
24613         
24614         // is ancestor a text element.
24615         var ac =  range.commonAncestorContainer;
24616         if (ac.nodeType == 3) {
24617             ac = ac.parentNode;
24618         }
24619         
24620         var ar = ac.childNodes;
24621          
24622         var nodes = [];
24623         var other_nodes = [];
24624         var has_other_nodes = false;
24625         for (var i=0;i<ar.length;i++) {
24626             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24627                 continue;
24628             }
24629             // fullly contained node.
24630             
24631             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24632                 nodes.push(ar[i]);
24633                 continue;
24634             }
24635             
24636             // probably selected..
24637             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24638                 other_nodes.push(ar[i]);
24639                 continue;
24640             }
24641             // outer..
24642             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24643                 continue;
24644             }
24645             
24646             
24647             has_other_nodes = true;
24648         }
24649         if (!nodes.length && other_nodes.length) {
24650             nodes= other_nodes;
24651         }
24652         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24653             return false;
24654         }
24655         
24656         return nodes[0];
24657     },
24658     createRange: function(sel)
24659     {
24660         // this has strange effects when using with 
24661         // top toolbar - not sure if it's a great idea.
24662         //this.editor.contentWindow.focus();
24663         if (typeof sel != "undefined") {
24664             try {
24665                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24666             } catch(e) {
24667                 return this.doc.createRange();
24668             }
24669         } else {
24670             return this.doc.createRange();
24671         }
24672     },
24673     getParentElement: function()
24674     {
24675         
24676         this.assignDocWin();
24677         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24678         
24679         var range = this.createRange(sel);
24680          
24681         try {
24682             var p = range.commonAncestorContainer;
24683             while (p.nodeType == 3) { // text node
24684                 p = p.parentNode;
24685             }
24686             return p;
24687         } catch (e) {
24688             return null;
24689         }
24690     
24691     },
24692     /***
24693      *
24694      * Range intersection.. the hard stuff...
24695      *  '-1' = before
24696      *  '0' = hits..
24697      *  '1' = after.
24698      *         [ -- selected range --- ]
24699      *   [fail]                        [fail]
24700      *
24701      *    basically..
24702      *      if end is before start or  hits it. fail.
24703      *      if start is after end or hits it fail.
24704      *
24705      *   if either hits (but other is outside. - then it's not 
24706      *   
24707      *    
24708      **/
24709     
24710     
24711     // @see http://www.thismuchiknow.co.uk/?p=64.
24712     rangeIntersectsNode : function(range, node)
24713     {
24714         var nodeRange = node.ownerDocument.createRange();
24715         try {
24716             nodeRange.selectNode(node);
24717         } catch (e) {
24718             nodeRange.selectNodeContents(node);
24719         }
24720     
24721         var rangeStartRange = range.cloneRange();
24722         rangeStartRange.collapse(true);
24723     
24724         var rangeEndRange = range.cloneRange();
24725         rangeEndRange.collapse(false);
24726     
24727         var nodeStartRange = nodeRange.cloneRange();
24728         nodeStartRange.collapse(true);
24729     
24730         var nodeEndRange = nodeRange.cloneRange();
24731         nodeEndRange.collapse(false);
24732     
24733         return rangeStartRange.compareBoundaryPoints(
24734                  Range.START_TO_START, nodeEndRange) == -1 &&
24735                rangeEndRange.compareBoundaryPoints(
24736                  Range.START_TO_START, nodeStartRange) == 1;
24737         
24738          
24739     },
24740     rangeCompareNode : function(range, node)
24741     {
24742         var nodeRange = node.ownerDocument.createRange();
24743         try {
24744             nodeRange.selectNode(node);
24745         } catch (e) {
24746             nodeRange.selectNodeContents(node);
24747         }
24748         
24749         
24750         range.collapse(true);
24751     
24752         nodeRange.collapse(true);
24753      
24754         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24755         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24756          
24757         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24758         
24759         var nodeIsBefore   =  ss == 1;
24760         var nodeIsAfter    = ee == -1;
24761         
24762         if (nodeIsBefore && nodeIsAfter) {
24763             return 0; // outer
24764         }
24765         if (!nodeIsBefore && nodeIsAfter) {
24766             return 1; //right trailed.
24767         }
24768         
24769         if (nodeIsBefore && !nodeIsAfter) {
24770             return 2;  // left trailed.
24771         }
24772         // fully contined.
24773         return 3;
24774     },
24775
24776     // private? - in a new class?
24777     cleanUpPaste :  function()
24778     {
24779         // cleans up the whole document..
24780         Roo.log('cleanuppaste');
24781         
24782         this.cleanUpChildren(this.doc.body);
24783         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24784         if (clean != this.doc.body.innerHTML) {
24785             this.doc.body.innerHTML = clean;
24786         }
24787         
24788     },
24789     
24790     cleanWordChars : function(input) {// change the chars to hex code
24791         var he = Roo.HtmlEditorCore;
24792         
24793         var output = input;
24794         Roo.each(he.swapCodes, function(sw) { 
24795             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24796             
24797             output = output.replace(swapper, sw[1]);
24798         });
24799         
24800         return output;
24801     },
24802     
24803     
24804     cleanUpChildren : function (n)
24805     {
24806         if (!n.childNodes.length) {
24807             return;
24808         }
24809         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24810            this.cleanUpChild(n.childNodes[i]);
24811         }
24812     },
24813     
24814     
24815         
24816     
24817     cleanUpChild : function (node)
24818     {
24819         var ed = this;
24820         //console.log(node);
24821         if (node.nodeName == "#text") {
24822             // clean up silly Windows -- stuff?
24823             return; 
24824         }
24825         if (node.nodeName == "#comment") {
24826             node.parentNode.removeChild(node);
24827             // clean up silly Windows -- stuff?
24828             return; 
24829         }
24830         var lcname = node.tagName.toLowerCase();
24831         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24832         // whitelist of tags..
24833         
24834         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24835             // remove node.
24836             node.parentNode.removeChild(node);
24837             return;
24838             
24839         }
24840         
24841         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24842         
24843         // spans with no attributes - just remove them..
24844         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24845             remove_keep_children = true;
24846         }
24847         
24848         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24849         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24850         
24851         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24852         //    remove_keep_children = true;
24853         //}
24854         
24855         if (remove_keep_children) {
24856             this.cleanUpChildren(node);
24857             // inserts everything just before this node...
24858             while (node.childNodes.length) {
24859                 var cn = node.childNodes[0];
24860                 node.removeChild(cn);
24861                 node.parentNode.insertBefore(cn, node);
24862             }
24863             node.parentNode.removeChild(node);
24864             return;
24865         }
24866         
24867         if (!node.attributes || !node.attributes.length) {
24868             
24869           
24870             
24871             
24872             this.cleanUpChildren(node);
24873             return;
24874         }
24875         
24876         function cleanAttr(n,v)
24877         {
24878             
24879             if (v.match(/^\./) || v.match(/^\//)) {
24880                 return;
24881             }
24882             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24883                 return;
24884             }
24885             if (v.match(/^#/)) {
24886                 return;
24887             }
24888             if (v.match(/^\{/)) { // allow template editing.
24889                 return;
24890             }
24891 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24892             node.removeAttribute(n);
24893             
24894         }
24895         
24896         var cwhite = this.cwhite;
24897         var cblack = this.cblack;
24898             
24899         function cleanStyle(n,v)
24900         {
24901             if (v.match(/expression/)) { //XSS?? should we even bother..
24902                 node.removeAttribute(n);
24903                 return;
24904             }
24905             
24906             var parts = v.split(/;/);
24907             var clean = [];
24908             
24909             Roo.each(parts, function(p) {
24910                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24911                 if (!p.length) {
24912                     return true;
24913                 }
24914                 var l = p.split(':').shift().replace(/\s+/g,'');
24915                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24916                 
24917                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24918 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24919                     //node.removeAttribute(n);
24920                     return true;
24921                 }
24922                 //Roo.log()
24923                 // only allow 'c whitelisted system attributes'
24924                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24925 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24926                     //node.removeAttribute(n);
24927                     return true;
24928                 }
24929                 
24930                 
24931                  
24932                 
24933                 clean.push(p);
24934                 return true;
24935             });
24936             if (clean.length) { 
24937                 node.setAttribute(n, clean.join(';'));
24938             } else {
24939                 node.removeAttribute(n);
24940             }
24941             
24942         }
24943         
24944         
24945         for (var i = node.attributes.length-1; i > -1 ; i--) {
24946             var a = node.attributes[i];
24947             //console.log(a);
24948             
24949             if (a.name.toLowerCase().substr(0,2)=='on')  {
24950                 node.removeAttribute(a.name);
24951                 continue;
24952             }
24953             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24954                 node.removeAttribute(a.name);
24955                 continue;
24956             }
24957             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24958                 cleanAttr(a.name,a.value); // fixme..
24959                 continue;
24960             }
24961             if (a.name == 'style') {
24962                 cleanStyle(a.name,a.value);
24963                 continue;
24964             }
24965             /// clean up MS crap..
24966             // tecnically this should be a list of valid class'es..
24967             
24968             
24969             if (a.name == 'class') {
24970                 if (a.value.match(/^Mso/)) {
24971                     node.removeAttribute('class');
24972                 }
24973                 
24974                 if (a.value.match(/^body$/)) {
24975                     node.removeAttribute('class');
24976                 }
24977                 continue;
24978             }
24979             
24980             // style cleanup!?
24981             // class cleanup?
24982             
24983         }
24984         
24985         
24986         this.cleanUpChildren(node);
24987         
24988         
24989     },
24990     
24991     /**
24992      * Clean up MS wordisms...
24993      */
24994     cleanWord : function(node)
24995     {
24996         if (!node) {
24997             this.cleanWord(this.doc.body);
24998             return;
24999         }
25000         
25001         if(
25002                 node.nodeName == 'SPAN' &&
25003                 !node.hasAttributes() &&
25004                 node.childNodes.length == 1 &&
25005                 node.firstChild.nodeName == "#text"  
25006         ) {
25007             var textNode = node.firstChild;
25008             node.removeChild(textNode);
25009             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25010                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25011             }
25012             node.parentNode.insertBefore(textNode, node);
25013             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25014                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25015             }
25016             node.parentNode.removeChild(node);
25017         }
25018         
25019         if (node.nodeName == "#text") {
25020             // clean up silly Windows -- stuff?
25021             return; 
25022         }
25023         if (node.nodeName == "#comment") {
25024             node.parentNode.removeChild(node);
25025             // clean up silly Windows -- stuff?
25026             return; 
25027         }
25028         
25029         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25030             node.parentNode.removeChild(node);
25031             return;
25032         }
25033         //Roo.log(node.tagName);
25034         // remove - but keep children..
25035         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25036             //Roo.log('-- removed');
25037             while (node.childNodes.length) {
25038                 var cn = node.childNodes[0];
25039                 node.removeChild(cn);
25040                 node.parentNode.insertBefore(cn, node);
25041                 // move node to parent - and clean it..
25042                 this.cleanWord(cn);
25043             }
25044             node.parentNode.removeChild(node);
25045             /// no need to iterate chidlren = it's got none..
25046             //this.iterateChildren(node, this.cleanWord);
25047             return;
25048         }
25049         // clean styles
25050         if (node.className.length) {
25051             
25052             var cn = node.className.split(/\W+/);
25053             var cna = [];
25054             Roo.each(cn, function(cls) {
25055                 if (cls.match(/Mso[a-zA-Z]+/)) {
25056                     return;
25057                 }
25058                 cna.push(cls);
25059             });
25060             node.className = cna.length ? cna.join(' ') : '';
25061             if (!cna.length) {
25062                 node.removeAttribute("class");
25063             }
25064         }
25065         
25066         if (node.hasAttribute("lang")) {
25067             node.removeAttribute("lang");
25068         }
25069         
25070         if (node.hasAttribute("style")) {
25071             
25072             var styles = node.getAttribute("style").split(";");
25073             var nstyle = [];
25074             Roo.each(styles, function(s) {
25075                 if (!s.match(/:/)) {
25076                     return;
25077                 }
25078                 var kv = s.split(":");
25079                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25080                     return;
25081                 }
25082                 // what ever is left... we allow.
25083                 nstyle.push(s);
25084             });
25085             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25086             if (!nstyle.length) {
25087                 node.removeAttribute('style');
25088             }
25089         }
25090         this.iterateChildren(node, this.cleanWord);
25091         
25092         
25093         
25094     },
25095     /**
25096      * iterateChildren of a Node, calling fn each time, using this as the scole..
25097      * @param {DomNode} node node to iterate children of.
25098      * @param {Function} fn method of this class to call on each item.
25099      */
25100     iterateChildren : function(node, fn)
25101     {
25102         if (!node.childNodes.length) {
25103                 return;
25104         }
25105         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25106            fn.call(this, node.childNodes[i])
25107         }
25108     },
25109     
25110     
25111     /**
25112      * cleanTableWidths.
25113      *
25114      * Quite often pasting from word etc.. results in tables with column and widths.
25115      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25116      *
25117      */
25118     cleanTableWidths : function(node)
25119     {
25120          
25121          
25122         if (!node) {
25123             this.cleanTableWidths(this.doc.body);
25124             return;
25125         }
25126         
25127         // ignore list...
25128         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25129             return; 
25130         }
25131         Roo.log(node.tagName);
25132         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25133             this.iterateChildren(node, this.cleanTableWidths);
25134             return;
25135         }
25136         if (node.hasAttribute('width')) {
25137             node.removeAttribute('width');
25138         }
25139         
25140          
25141         if (node.hasAttribute("style")) {
25142             // pretty basic...
25143             
25144             var styles = node.getAttribute("style").split(";");
25145             var nstyle = [];
25146             Roo.each(styles, function(s) {
25147                 if (!s.match(/:/)) {
25148                     return;
25149                 }
25150                 var kv = s.split(":");
25151                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25152                     return;
25153                 }
25154                 // what ever is left... we allow.
25155                 nstyle.push(s);
25156             });
25157             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25158             if (!nstyle.length) {
25159                 node.removeAttribute('style');
25160             }
25161         }
25162         
25163         this.iterateChildren(node, this.cleanTableWidths);
25164         
25165         
25166     },
25167     
25168     
25169     
25170     
25171     domToHTML : function(currentElement, depth, nopadtext) {
25172         
25173         depth = depth || 0;
25174         nopadtext = nopadtext || false;
25175     
25176         if (!currentElement) {
25177             return this.domToHTML(this.doc.body);
25178         }
25179         
25180         //Roo.log(currentElement);
25181         var j;
25182         var allText = false;
25183         var nodeName = currentElement.nodeName;
25184         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25185         
25186         if  (nodeName == '#text') {
25187             
25188             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25189         }
25190         
25191         
25192         var ret = '';
25193         if (nodeName != 'BODY') {
25194              
25195             var i = 0;
25196             // Prints the node tagName, such as <A>, <IMG>, etc
25197             if (tagName) {
25198                 var attr = [];
25199                 for(i = 0; i < currentElement.attributes.length;i++) {
25200                     // quoting?
25201                     var aname = currentElement.attributes.item(i).name;
25202                     if (!currentElement.attributes.item(i).value.length) {
25203                         continue;
25204                     }
25205                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25206                 }
25207                 
25208                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25209             } 
25210             else {
25211                 
25212                 // eack
25213             }
25214         } else {
25215             tagName = false;
25216         }
25217         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25218             return ret;
25219         }
25220         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25221             nopadtext = true;
25222         }
25223         
25224         
25225         // Traverse the tree
25226         i = 0;
25227         var currentElementChild = currentElement.childNodes.item(i);
25228         var allText = true;
25229         var innerHTML  = '';
25230         lastnode = '';
25231         while (currentElementChild) {
25232             // Formatting code (indent the tree so it looks nice on the screen)
25233             var nopad = nopadtext;
25234             if (lastnode == 'SPAN') {
25235                 nopad  = true;
25236             }
25237             // text
25238             if  (currentElementChild.nodeName == '#text') {
25239                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25240                 toadd = nopadtext ? toadd : toadd.trim();
25241                 if (!nopad && toadd.length > 80) {
25242                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25243                 }
25244                 innerHTML  += toadd;
25245                 
25246                 i++;
25247                 currentElementChild = currentElement.childNodes.item(i);
25248                 lastNode = '';
25249                 continue;
25250             }
25251             allText = false;
25252             
25253             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25254                 
25255             // Recursively traverse the tree structure of the child node
25256             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25257             lastnode = currentElementChild.nodeName;
25258             i++;
25259             currentElementChild=currentElement.childNodes.item(i);
25260         }
25261         
25262         ret += innerHTML;
25263         
25264         if (!allText) {
25265                 // The remaining code is mostly for formatting the tree
25266             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25267         }
25268         
25269         
25270         if (tagName) {
25271             ret+= "</"+tagName+">";
25272         }
25273         return ret;
25274         
25275     },
25276         
25277     applyBlacklists : function()
25278     {
25279         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25280         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25281         
25282         this.white = [];
25283         this.black = [];
25284         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25285             if (b.indexOf(tag) > -1) {
25286                 return;
25287             }
25288             this.white.push(tag);
25289             
25290         }, this);
25291         
25292         Roo.each(w, function(tag) {
25293             if (b.indexOf(tag) > -1) {
25294                 return;
25295             }
25296             if (this.white.indexOf(tag) > -1) {
25297                 return;
25298             }
25299             this.white.push(tag);
25300             
25301         }, this);
25302         
25303         
25304         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25305             if (w.indexOf(tag) > -1) {
25306                 return;
25307             }
25308             this.black.push(tag);
25309             
25310         }, this);
25311         
25312         Roo.each(b, function(tag) {
25313             if (w.indexOf(tag) > -1) {
25314                 return;
25315             }
25316             if (this.black.indexOf(tag) > -1) {
25317                 return;
25318             }
25319             this.black.push(tag);
25320             
25321         }, this);
25322         
25323         
25324         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25325         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25326         
25327         this.cwhite = [];
25328         this.cblack = [];
25329         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25330             if (b.indexOf(tag) > -1) {
25331                 return;
25332             }
25333             this.cwhite.push(tag);
25334             
25335         }, this);
25336         
25337         Roo.each(w, function(tag) {
25338             if (b.indexOf(tag) > -1) {
25339                 return;
25340             }
25341             if (this.cwhite.indexOf(tag) > -1) {
25342                 return;
25343             }
25344             this.cwhite.push(tag);
25345             
25346         }, this);
25347         
25348         
25349         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25350             if (w.indexOf(tag) > -1) {
25351                 return;
25352             }
25353             this.cblack.push(tag);
25354             
25355         }, this);
25356         
25357         Roo.each(b, function(tag) {
25358             if (w.indexOf(tag) > -1) {
25359                 return;
25360             }
25361             if (this.cblack.indexOf(tag) > -1) {
25362                 return;
25363             }
25364             this.cblack.push(tag);
25365             
25366         }, this);
25367     },
25368     
25369     setStylesheets : function(stylesheets)
25370     {
25371         if(typeof(stylesheets) == 'string'){
25372             Roo.get(this.iframe.contentDocument.head).createChild({
25373                 tag : 'link',
25374                 rel : 'stylesheet',
25375                 type : 'text/css',
25376                 href : stylesheets
25377             });
25378             
25379             return;
25380         }
25381         var _this = this;
25382      
25383         Roo.each(stylesheets, function(s) {
25384             if(!s.length){
25385                 return;
25386             }
25387             
25388             Roo.get(_this.iframe.contentDocument.head).createChild({
25389                 tag : 'link',
25390                 rel : 'stylesheet',
25391                 type : 'text/css',
25392                 href : s
25393             });
25394         });
25395
25396         
25397     },
25398     
25399     removeStylesheets : function()
25400     {
25401         var _this = this;
25402         
25403         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25404             s.remove();
25405         });
25406     },
25407     
25408     setStyle : function(style)
25409     {
25410         Roo.get(this.iframe.contentDocument.head).createChild({
25411             tag : 'style',
25412             type : 'text/css',
25413             html : style
25414         });
25415
25416         return;
25417     }
25418     
25419     // hide stuff that is not compatible
25420     /**
25421      * @event blur
25422      * @hide
25423      */
25424     /**
25425      * @event change
25426      * @hide
25427      */
25428     /**
25429      * @event focus
25430      * @hide
25431      */
25432     /**
25433      * @event specialkey
25434      * @hide
25435      */
25436     /**
25437      * @cfg {String} fieldClass @hide
25438      */
25439     /**
25440      * @cfg {String} focusClass @hide
25441      */
25442     /**
25443      * @cfg {String} autoCreate @hide
25444      */
25445     /**
25446      * @cfg {String} inputType @hide
25447      */
25448     /**
25449      * @cfg {String} invalidClass @hide
25450      */
25451     /**
25452      * @cfg {String} invalidText @hide
25453      */
25454     /**
25455      * @cfg {String} msgFx @hide
25456      */
25457     /**
25458      * @cfg {String} validateOnBlur @hide
25459      */
25460 });
25461
25462 Roo.HtmlEditorCore.white = [
25463         'area', 'br', 'img', 'input', 'hr', 'wbr',
25464         
25465        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25466        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25467        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25468        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25469        'table',   'ul',         'xmp', 
25470        
25471        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25472       'thead',   'tr', 
25473      
25474       'dir', 'menu', 'ol', 'ul', 'dl',
25475        
25476       'embed',  'object'
25477 ];
25478
25479
25480 Roo.HtmlEditorCore.black = [
25481     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25482         'applet', // 
25483         'base',   'basefont', 'bgsound', 'blink',  'body', 
25484         'frame',  'frameset', 'head',    'html',   'ilayer', 
25485         'iframe', 'layer',  'link',     'meta',    'object',   
25486         'script', 'style' ,'title',  'xml' // clean later..
25487 ];
25488 Roo.HtmlEditorCore.clean = [
25489     'script', 'style', 'title', 'xml'
25490 ];
25491 Roo.HtmlEditorCore.remove = [
25492     'font'
25493 ];
25494 // attributes..
25495
25496 Roo.HtmlEditorCore.ablack = [
25497     'on'
25498 ];
25499     
25500 Roo.HtmlEditorCore.aclean = [ 
25501     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25502 ];
25503
25504 // protocols..
25505 Roo.HtmlEditorCore.pwhite= [
25506         'http',  'https',  'mailto'
25507 ];
25508
25509 // white listed style attributes.
25510 Roo.HtmlEditorCore.cwhite= [
25511       //  'text-align', /// default is to allow most things..
25512       
25513          
25514 //        'font-size'//??
25515 ];
25516
25517 // black listed style attributes.
25518 Roo.HtmlEditorCore.cblack= [
25519       //  'font-size' -- this can be set by the project 
25520 ];
25521
25522
25523 Roo.HtmlEditorCore.swapCodes   =[ 
25524     [    8211, "--" ], 
25525     [    8212, "--" ], 
25526     [    8216,  "'" ],  
25527     [    8217, "'" ],  
25528     [    8220, '"' ],  
25529     [    8221, '"' ],  
25530     [    8226, "*" ],  
25531     [    8230, "..." ]
25532 ]; 
25533
25534     /*
25535  * - LGPL
25536  *
25537  * HtmlEditor
25538  * 
25539  */
25540
25541 /**
25542  * @class Roo.bootstrap.HtmlEditor
25543  * @extends Roo.bootstrap.TextArea
25544  * Bootstrap HtmlEditor class
25545
25546  * @constructor
25547  * Create a new HtmlEditor
25548  * @param {Object} config The config object
25549  */
25550
25551 Roo.bootstrap.HtmlEditor = function(config){
25552     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25553     if (!this.toolbars) {
25554         this.toolbars = [];
25555     }
25556     
25557     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25558     this.addEvents({
25559             /**
25560              * @event initialize
25561              * Fires when the editor is fully initialized (including the iframe)
25562              * @param {HtmlEditor} this
25563              */
25564             initialize: true,
25565             /**
25566              * @event activate
25567              * Fires when the editor is first receives the focus. Any insertion must wait
25568              * until after this event.
25569              * @param {HtmlEditor} this
25570              */
25571             activate: true,
25572              /**
25573              * @event beforesync
25574              * Fires before the textarea is updated with content from the editor iframe. Return false
25575              * to cancel the sync.
25576              * @param {HtmlEditor} this
25577              * @param {String} html
25578              */
25579             beforesync: true,
25580              /**
25581              * @event beforepush
25582              * Fires before the iframe editor is updated with content from the textarea. Return false
25583              * to cancel the push.
25584              * @param {HtmlEditor} this
25585              * @param {String} html
25586              */
25587             beforepush: true,
25588              /**
25589              * @event sync
25590              * Fires when the textarea is updated with content from the editor iframe.
25591              * @param {HtmlEditor} this
25592              * @param {String} html
25593              */
25594             sync: true,
25595              /**
25596              * @event push
25597              * Fires when the iframe editor is updated with content from the textarea.
25598              * @param {HtmlEditor} this
25599              * @param {String} html
25600              */
25601             push: true,
25602              /**
25603              * @event editmodechange
25604              * Fires when the editor switches edit modes
25605              * @param {HtmlEditor} this
25606              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25607              */
25608             editmodechange: true,
25609             /**
25610              * @event editorevent
25611              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25612              * @param {HtmlEditor} this
25613              */
25614             editorevent: true,
25615             /**
25616              * @event firstfocus
25617              * Fires when on first focus - needed by toolbars..
25618              * @param {HtmlEditor} this
25619              */
25620             firstfocus: true,
25621             /**
25622              * @event autosave
25623              * Auto save the htmlEditor value as a file into Events
25624              * @param {HtmlEditor} this
25625              */
25626             autosave: true,
25627             /**
25628              * @event savedpreview
25629              * preview the saved version of htmlEditor
25630              * @param {HtmlEditor} this
25631              */
25632             savedpreview: true
25633         });
25634 };
25635
25636
25637 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25638     
25639     
25640       /**
25641      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25642      */
25643     toolbars : false,
25644     
25645      /**
25646     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25647     */
25648     btns : [],
25649    
25650      /**
25651      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25652      *                        Roo.resizable.
25653      */
25654     resizable : false,
25655      /**
25656      * @cfg {Number} height (in pixels)
25657      */   
25658     height: 300,
25659    /**
25660      * @cfg {Number} width (in pixels)
25661      */   
25662     width: false,
25663     
25664     /**
25665      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25666      * 
25667      */
25668     stylesheets: false,
25669     
25670     // id of frame..
25671     frameId: false,
25672     
25673     // private properties
25674     validationEvent : false,
25675     deferHeight: true,
25676     initialized : false,
25677     activated : false,
25678     
25679     onFocus : Roo.emptyFn,
25680     iframePad:3,
25681     hideMode:'offsets',
25682     
25683     tbContainer : false,
25684     
25685     bodyCls : '',
25686     
25687     toolbarContainer :function() {
25688         return this.wrap.select('.x-html-editor-tb',true).first();
25689     },
25690
25691     /**
25692      * Protected method that will not generally be called directly. It
25693      * is called when the editor creates its toolbar. Override this method if you need to
25694      * add custom toolbar buttons.
25695      * @param {HtmlEditor} editor
25696      */
25697     createToolbar : function(){
25698         Roo.log('renewing');
25699         Roo.log("create toolbars");
25700         
25701         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25702         this.toolbars[0].render(this.toolbarContainer());
25703         
25704         return;
25705         
25706 //        if (!editor.toolbars || !editor.toolbars.length) {
25707 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25708 //        }
25709 //        
25710 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25711 //            editor.toolbars[i] = Roo.factory(
25712 //                    typeof(editor.toolbars[i]) == 'string' ?
25713 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25714 //                Roo.bootstrap.HtmlEditor);
25715 //            editor.toolbars[i].init(editor);
25716 //        }
25717     },
25718
25719      
25720     // private
25721     onRender : function(ct, position)
25722     {
25723        // Roo.log("Call onRender: " + this.xtype);
25724         var _t = this;
25725         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25726       
25727         this.wrap = this.inputEl().wrap({
25728             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25729         });
25730         
25731         this.editorcore.onRender(ct, position);
25732          
25733         if (this.resizable) {
25734             this.resizeEl = new Roo.Resizable(this.wrap, {
25735                 pinned : true,
25736                 wrap: true,
25737                 dynamic : true,
25738                 minHeight : this.height,
25739                 height: this.height,
25740                 handles : this.resizable,
25741                 width: this.width,
25742                 listeners : {
25743                     resize : function(r, w, h) {
25744                         _t.onResize(w,h); // -something
25745                     }
25746                 }
25747             });
25748             
25749         }
25750         this.createToolbar(this);
25751        
25752         
25753         if(!this.width && this.resizable){
25754             this.setSize(this.wrap.getSize());
25755         }
25756         if (this.resizeEl) {
25757             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25758             // should trigger onReize..
25759         }
25760         
25761     },
25762
25763     // private
25764     onResize : function(w, h)
25765     {
25766         Roo.log('resize: ' +w + ',' + h );
25767         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25768         var ew = false;
25769         var eh = false;
25770         
25771         if(this.inputEl() ){
25772             if(typeof w == 'number'){
25773                 var aw = w - this.wrap.getFrameWidth('lr');
25774                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25775                 ew = aw;
25776             }
25777             if(typeof h == 'number'){
25778                  var tbh = -11;  // fixme it needs to tool bar size!
25779                 for (var i =0; i < this.toolbars.length;i++) {
25780                     // fixme - ask toolbars for heights?
25781                     tbh += this.toolbars[i].el.getHeight();
25782                     //if (this.toolbars[i].footer) {
25783                     //    tbh += this.toolbars[i].footer.el.getHeight();
25784                     //}
25785                 }
25786               
25787                 
25788                 
25789                 
25790                 
25791                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25792                 ah -= 5; // knock a few pixes off for look..
25793                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25794                 var eh = ah;
25795             }
25796         }
25797         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25798         this.editorcore.onResize(ew,eh);
25799         
25800     },
25801
25802     /**
25803      * Toggles the editor between standard and source edit mode.
25804      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25805      */
25806     toggleSourceEdit : function(sourceEditMode)
25807     {
25808         this.editorcore.toggleSourceEdit(sourceEditMode);
25809         
25810         if(this.editorcore.sourceEditMode){
25811             Roo.log('editor - showing textarea');
25812             
25813 //            Roo.log('in');
25814 //            Roo.log(this.syncValue());
25815             this.syncValue();
25816             this.inputEl().removeClass(['hide', 'x-hidden']);
25817             this.inputEl().dom.removeAttribute('tabIndex');
25818             this.inputEl().focus();
25819         }else{
25820             Roo.log('editor - hiding textarea');
25821 //            Roo.log('out')
25822 //            Roo.log(this.pushValue()); 
25823             this.pushValue();
25824             
25825             this.inputEl().addClass(['hide', 'x-hidden']);
25826             this.inputEl().dom.setAttribute('tabIndex', -1);
25827             //this.deferFocus();
25828         }
25829          
25830         if(this.resizable){
25831             this.setSize(this.wrap.getSize());
25832         }
25833         
25834         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25835     },
25836  
25837     // private (for BoxComponent)
25838     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25839
25840     // private (for BoxComponent)
25841     getResizeEl : function(){
25842         return this.wrap;
25843     },
25844
25845     // private (for BoxComponent)
25846     getPositionEl : function(){
25847         return this.wrap;
25848     },
25849
25850     // private
25851     initEvents : function(){
25852         this.originalValue = this.getValue();
25853     },
25854
25855 //    /**
25856 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25857 //     * @method
25858 //     */
25859 //    markInvalid : Roo.emptyFn,
25860 //    /**
25861 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25862 //     * @method
25863 //     */
25864 //    clearInvalid : Roo.emptyFn,
25865
25866     setValue : function(v){
25867         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25868         this.editorcore.pushValue();
25869     },
25870
25871      
25872     // private
25873     deferFocus : function(){
25874         this.focus.defer(10, this);
25875     },
25876
25877     // doc'ed in Field
25878     focus : function(){
25879         this.editorcore.focus();
25880         
25881     },
25882       
25883
25884     // private
25885     onDestroy : function(){
25886         
25887         
25888         
25889         if(this.rendered){
25890             
25891             for (var i =0; i < this.toolbars.length;i++) {
25892                 // fixme - ask toolbars for heights?
25893                 this.toolbars[i].onDestroy();
25894             }
25895             
25896             this.wrap.dom.innerHTML = '';
25897             this.wrap.remove();
25898         }
25899     },
25900
25901     // private
25902     onFirstFocus : function(){
25903         //Roo.log("onFirstFocus");
25904         this.editorcore.onFirstFocus();
25905          for (var i =0; i < this.toolbars.length;i++) {
25906             this.toolbars[i].onFirstFocus();
25907         }
25908         
25909     },
25910     
25911     // private
25912     syncValue : function()
25913     {   
25914         this.editorcore.syncValue();
25915     },
25916     
25917     pushValue : function()
25918     {   
25919         this.editorcore.pushValue();
25920     }
25921      
25922     
25923     // hide stuff that is not compatible
25924     /**
25925      * @event blur
25926      * @hide
25927      */
25928     /**
25929      * @event change
25930      * @hide
25931      */
25932     /**
25933      * @event focus
25934      * @hide
25935      */
25936     /**
25937      * @event specialkey
25938      * @hide
25939      */
25940     /**
25941      * @cfg {String} fieldClass @hide
25942      */
25943     /**
25944      * @cfg {String} focusClass @hide
25945      */
25946     /**
25947      * @cfg {String} autoCreate @hide
25948      */
25949     /**
25950      * @cfg {String} inputType @hide
25951      */
25952      
25953     /**
25954      * @cfg {String} invalidText @hide
25955      */
25956     /**
25957      * @cfg {String} msgFx @hide
25958      */
25959     /**
25960      * @cfg {String} validateOnBlur @hide
25961      */
25962 });
25963  
25964     
25965    
25966    
25967    
25968       
25969 Roo.namespace('Roo.bootstrap.htmleditor');
25970 /**
25971  * @class Roo.bootstrap.HtmlEditorToolbar1
25972  * Basic Toolbar
25973  * 
25974  * @example
25975  * Usage:
25976  *
25977  new Roo.bootstrap.HtmlEditor({
25978     ....
25979     toolbars : [
25980         new Roo.bootstrap.HtmlEditorToolbar1({
25981             disable : { fonts: 1 , format: 1, ..., ... , ...],
25982             btns : [ .... ]
25983         })
25984     }
25985      
25986  * 
25987  * @cfg {Object} disable List of elements to disable..
25988  * @cfg {Array} btns List of additional buttons.
25989  * 
25990  * 
25991  * NEEDS Extra CSS? 
25992  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25993  */
25994  
25995 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25996 {
25997     
25998     Roo.apply(this, config);
25999     
26000     // default disabled, based on 'good practice'..
26001     this.disable = this.disable || {};
26002     Roo.applyIf(this.disable, {
26003         fontSize : true,
26004         colors : true,
26005         specialElements : true
26006     });
26007     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26008     
26009     this.editor = config.editor;
26010     this.editorcore = config.editor.editorcore;
26011     
26012     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26013     
26014     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26015     // dont call parent... till later.
26016 }
26017 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26018      
26019     bar : true,
26020     
26021     editor : false,
26022     editorcore : false,
26023     
26024     
26025     formats : [
26026         "p" ,  
26027         "h1","h2","h3","h4","h5","h6", 
26028         "pre", "code", 
26029         "abbr", "acronym", "address", "cite", "samp", "var",
26030         'div','span'
26031     ],
26032     
26033     onRender : function(ct, position)
26034     {
26035        // Roo.log("Call onRender: " + this.xtype);
26036         
26037        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26038        Roo.log(this.el);
26039        this.el.dom.style.marginBottom = '0';
26040        var _this = this;
26041        var editorcore = this.editorcore;
26042        var editor= this.editor;
26043        
26044        var children = [];
26045        var btn = function(id,cmd , toggle, handler, html){
26046        
26047             var  event = toggle ? 'toggle' : 'click';
26048        
26049             var a = {
26050                 size : 'sm',
26051                 xtype: 'Button',
26052                 xns: Roo.bootstrap,
26053                 //glyphicon : id,
26054                 fa: id,
26055                 cmd : id || cmd,
26056                 enableToggle:toggle !== false,
26057                 html : html || '',
26058                 pressed : toggle ? false : null,
26059                 listeners : {}
26060             };
26061             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26062                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26063             };
26064             children.push(a);
26065             return a;
26066        }
26067        
26068     //    var cb_box = function...
26069         
26070         var style = {
26071                 xtype: 'Button',
26072                 size : 'sm',
26073                 xns: Roo.bootstrap,
26074                 fa : 'font',
26075                 //html : 'submit'
26076                 menu : {
26077                     xtype: 'Menu',
26078                     xns: Roo.bootstrap,
26079                     items:  []
26080                 }
26081         };
26082         Roo.each(this.formats, function(f) {
26083             style.menu.items.push({
26084                 xtype :'MenuItem',
26085                 xns: Roo.bootstrap,
26086                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26087                 tagname : f,
26088                 listeners : {
26089                     click : function()
26090                     {
26091                         editorcore.insertTag(this.tagname);
26092                         editor.focus();
26093                     }
26094                 }
26095                 
26096             });
26097         });
26098         children.push(style);   
26099         
26100         btn('bold',false,true);
26101         btn('italic',false,true);
26102         btn('align-left', 'justifyleft',true);
26103         btn('align-center', 'justifycenter',true);
26104         btn('align-right' , 'justifyright',true);
26105         btn('link', false, false, function(btn) {
26106             //Roo.log("create link?");
26107             var url = prompt(this.createLinkText, this.defaultLinkValue);
26108             if(url && url != 'http:/'+'/'){
26109                 this.editorcore.relayCmd('createlink', url);
26110             }
26111         }),
26112         btn('list','insertunorderedlist',true);
26113         btn('pencil', false,true, function(btn){
26114                 Roo.log(this);
26115                 this.toggleSourceEdit(btn.pressed);
26116         });
26117         
26118         if (this.editor.btns.length > 0) {
26119             for (var i = 0; i<this.editor.btns.length; i++) {
26120                 children.push(this.editor.btns[i]);
26121             }
26122         }
26123         
26124         /*
26125         var cog = {
26126                 xtype: 'Button',
26127                 size : 'sm',
26128                 xns: Roo.bootstrap,
26129                 glyphicon : 'cog',
26130                 //html : 'submit'
26131                 menu : {
26132                     xtype: 'Menu',
26133                     xns: Roo.bootstrap,
26134                     items:  []
26135                 }
26136         };
26137         
26138         cog.menu.items.push({
26139             xtype :'MenuItem',
26140             xns: Roo.bootstrap,
26141             html : Clean styles,
26142             tagname : f,
26143             listeners : {
26144                 click : function()
26145                 {
26146                     editorcore.insertTag(this.tagname);
26147                     editor.focus();
26148                 }
26149             }
26150             
26151         });
26152        */
26153         
26154          
26155        this.xtype = 'NavSimplebar';
26156         
26157         for(var i=0;i< children.length;i++) {
26158             
26159             this.buttons.add(this.addxtypeChild(children[i]));
26160             
26161         }
26162         
26163         editor.on('editorevent', this.updateToolbar, this);
26164     },
26165     onBtnClick : function(id)
26166     {
26167        this.editorcore.relayCmd(id);
26168        this.editorcore.focus();
26169     },
26170     
26171     /**
26172      * Protected method that will not generally be called directly. It triggers
26173      * a toolbar update by reading the markup state of the current selection in the editor.
26174      */
26175     updateToolbar: function(){
26176
26177         if(!this.editorcore.activated){
26178             this.editor.onFirstFocus(); // is this neeed?
26179             return;
26180         }
26181
26182         var btns = this.buttons; 
26183         var doc = this.editorcore.doc;
26184         btns.get('bold').setActive(doc.queryCommandState('bold'));
26185         btns.get('italic').setActive(doc.queryCommandState('italic'));
26186         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26187         
26188         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26189         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26190         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26191         
26192         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26193         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26194          /*
26195         
26196         var ans = this.editorcore.getAllAncestors();
26197         if (this.formatCombo) {
26198             
26199             
26200             var store = this.formatCombo.store;
26201             this.formatCombo.setValue("");
26202             for (var i =0; i < ans.length;i++) {
26203                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26204                     // select it..
26205                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26206                     break;
26207                 }
26208             }
26209         }
26210         
26211         
26212         
26213         // hides menus... - so this cant be on a menu...
26214         Roo.bootstrap.MenuMgr.hideAll();
26215         */
26216         Roo.bootstrap.MenuMgr.hideAll();
26217         //this.editorsyncValue();
26218     },
26219     onFirstFocus: function() {
26220         this.buttons.each(function(item){
26221            item.enable();
26222         });
26223     },
26224     toggleSourceEdit : function(sourceEditMode){
26225         
26226           
26227         if(sourceEditMode){
26228             Roo.log("disabling buttons");
26229            this.buttons.each( function(item){
26230                 if(item.cmd != 'pencil'){
26231                     item.disable();
26232                 }
26233             });
26234           
26235         }else{
26236             Roo.log("enabling buttons");
26237             if(this.editorcore.initialized){
26238                 this.buttons.each( function(item){
26239                     item.enable();
26240                 });
26241             }
26242             
26243         }
26244         Roo.log("calling toggole on editor");
26245         // tell the editor that it's been pressed..
26246         this.editor.toggleSourceEdit(sourceEditMode);
26247        
26248     }
26249 });
26250
26251
26252
26253
26254  
26255 /*
26256  * - LGPL
26257  */
26258
26259 /**
26260  * @class Roo.bootstrap.Markdown
26261  * @extends Roo.bootstrap.TextArea
26262  * Bootstrap Showdown editable area
26263  * @cfg {string} content
26264  * 
26265  * @constructor
26266  * Create a new Showdown
26267  */
26268
26269 Roo.bootstrap.Markdown = function(config){
26270     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26271    
26272 };
26273
26274 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26275     
26276     editing :false,
26277     
26278     initEvents : function()
26279     {
26280         
26281         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26282         this.markdownEl = this.el.createChild({
26283             cls : 'roo-markdown-area'
26284         });
26285         this.inputEl().addClass('d-none');
26286         if (this.getValue() == '') {
26287             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26288             
26289         } else {
26290             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26291         }
26292         this.markdownEl.on('click', this.toggleTextEdit, this);
26293         this.on('blur', this.toggleTextEdit, this);
26294         this.on('specialkey', this.resizeTextArea, this);
26295     },
26296     
26297     toggleTextEdit : function()
26298     {
26299         var sh = this.markdownEl.getHeight();
26300         this.inputEl().addClass('d-none');
26301         this.markdownEl.addClass('d-none');
26302         if (!this.editing) {
26303             // show editor?
26304             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26305             this.inputEl().removeClass('d-none');
26306             this.inputEl().focus();
26307             this.editing = true;
26308             return;
26309         }
26310         // show showdown...
26311         this.updateMarkdown();
26312         this.markdownEl.removeClass('d-none');
26313         this.editing = false;
26314         return;
26315     },
26316     updateMarkdown : function()
26317     {
26318         if (this.getValue() == '') {
26319             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26320             return;
26321         }
26322  
26323         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26324     },
26325     
26326     resizeTextArea: function () {
26327         
26328         var sh = 100;
26329         Roo.log([sh, this.getValue().split("\n").length * 30]);
26330         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26331     },
26332     setValue : function(val)
26333     {
26334         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26335         if (!this.editing) {
26336             this.updateMarkdown();
26337         }
26338         
26339     },
26340     focus : function()
26341     {
26342         if (!this.editing) {
26343             this.toggleTextEdit();
26344         }
26345         
26346     }
26347
26348
26349 });
26350 /**
26351  * @class Roo.bootstrap.Table.AbstractSelectionModel
26352  * @extends Roo.util.Observable
26353  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26354  * implemented by descendant classes.  This class should not be directly instantiated.
26355  * @constructor
26356  */
26357 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26358     this.locked = false;
26359     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26360 };
26361
26362
26363 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26364     /** @ignore Called by the grid automatically. Do not call directly. */
26365     init : function(grid){
26366         this.grid = grid;
26367         this.initEvents();
26368     },
26369
26370     /**
26371      * Locks the selections.
26372      */
26373     lock : function(){
26374         this.locked = true;
26375     },
26376
26377     /**
26378      * Unlocks the selections.
26379      */
26380     unlock : function(){
26381         this.locked = false;
26382     },
26383
26384     /**
26385      * Returns true if the selections are locked.
26386      * @return {Boolean}
26387      */
26388     isLocked : function(){
26389         return this.locked;
26390     },
26391     
26392     
26393     initEvents : function ()
26394     {
26395         
26396     }
26397 });
26398 /**
26399  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26400  * @class Roo.bootstrap.Table.RowSelectionModel
26401  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26402  * It supports multiple selections and keyboard selection/navigation. 
26403  * @constructor
26404  * @param {Object} config
26405  */
26406
26407 Roo.bootstrap.Table.RowSelectionModel = function(config){
26408     Roo.apply(this, config);
26409     this.selections = new Roo.util.MixedCollection(false, function(o){
26410         return o.id;
26411     });
26412
26413     this.last = false;
26414     this.lastActive = false;
26415
26416     this.addEvents({
26417         /**
26418              * @event selectionchange
26419              * Fires when the selection changes
26420              * @param {SelectionModel} this
26421              */
26422             "selectionchange" : true,
26423         /**
26424              * @event afterselectionchange
26425              * Fires after the selection changes (eg. by key press or clicking)
26426              * @param {SelectionModel} this
26427              */
26428             "afterselectionchange" : true,
26429         /**
26430              * @event beforerowselect
26431              * Fires when a row is selected being selected, return false to cancel.
26432              * @param {SelectionModel} this
26433              * @param {Number} rowIndex The selected index
26434              * @param {Boolean} keepExisting False if other selections will be cleared
26435              */
26436             "beforerowselect" : true,
26437         /**
26438              * @event rowselect
26439              * Fires when a row is selected.
26440              * @param {SelectionModel} this
26441              * @param {Number} rowIndex The selected index
26442              * @param {Roo.data.Record} r The record
26443              */
26444             "rowselect" : true,
26445         /**
26446              * @event rowdeselect
26447              * Fires when a row is deselected.
26448              * @param {SelectionModel} this
26449              * @param {Number} rowIndex The selected index
26450              */
26451         "rowdeselect" : true
26452     });
26453     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26454     this.locked = false;
26455  };
26456
26457 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26458     /**
26459      * @cfg {Boolean} singleSelect
26460      * True to allow selection of only one row at a time (defaults to false)
26461      */
26462     singleSelect : false,
26463
26464     // private
26465     initEvents : function()
26466     {
26467
26468         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26469         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26470         //}else{ // allow click to work like normal
26471          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26472         //}
26473         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26474         this.grid.on("rowclick", this.handleMouseDown, this);
26475         
26476         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26477             "up" : function(e){
26478                 if(!e.shiftKey){
26479                     this.selectPrevious(e.shiftKey);
26480                 }else if(this.last !== false && this.lastActive !== false){
26481                     var last = this.last;
26482                     this.selectRange(this.last,  this.lastActive-1);
26483                     this.grid.getView().focusRow(this.lastActive);
26484                     if(last !== false){
26485                         this.last = last;
26486                     }
26487                 }else{
26488                     this.selectFirstRow();
26489                 }
26490                 this.fireEvent("afterselectionchange", this);
26491             },
26492             "down" : function(e){
26493                 if(!e.shiftKey){
26494                     this.selectNext(e.shiftKey);
26495                 }else if(this.last !== false && this.lastActive !== false){
26496                     var last = this.last;
26497                     this.selectRange(this.last,  this.lastActive+1);
26498                     this.grid.getView().focusRow(this.lastActive);
26499                     if(last !== false){
26500                         this.last = last;
26501                     }
26502                 }else{
26503                     this.selectFirstRow();
26504                 }
26505                 this.fireEvent("afterselectionchange", this);
26506             },
26507             scope: this
26508         });
26509         this.grid.store.on('load', function(){
26510             this.selections.clear();
26511         },this);
26512         /*
26513         var view = this.grid.view;
26514         view.on("refresh", this.onRefresh, this);
26515         view.on("rowupdated", this.onRowUpdated, this);
26516         view.on("rowremoved", this.onRemove, this);
26517         */
26518     },
26519
26520     // private
26521     onRefresh : function()
26522     {
26523         var ds = this.grid.store, i, v = this.grid.view;
26524         var s = this.selections;
26525         s.each(function(r){
26526             if((i = ds.indexOfId(r.id)) != -1){
26527                 v.onRowSelect(i);
26528             }else{
26529                 s.remove(r);
26530             }
26531         });
26532     },
26533
26534     // private
26535     onRemove : function(v, index, r){
26536         this.selections.remove(r);
26537     },
26538
26539     // private
26540     onRowUpdated : function(v, index, r){
26541         if(this.isSelected(r)){
26542             v.onRowSelect(index);
26543         }
26544     },
26545
26546     /**
26547      * Select records.
26548      * @param {Array} records The records to select
26549      * @param {Boolean} keepExisting (optional) True to keep existing selections
26550      */
26551     selectRecords : function(records, keepExisting)
26552     {
26553         if(!keepExisting){
26554             this.clearSelections();
26555         }
26556             var ds = this.grid.store;
26557         for(var i = 0, len = records.length; i < len; i++){
26558             this.selectRow(ds.indexOf(records[i]), true);
26559         }
26560     },
26561
26562     /**
26563      * Gets the number of selected rows.
26564      * @return {Number}
26565      */
26566     getCount : function(){
26567         return this.selections.length;
26568     },
26569
26570     /**
26571      * Selects the first row in the grid.
26572      */
26573     selectFirstRow : function(){
26574         this.selectRow(0);
26575     },
26576
26577     /**
26578      * Select the last row.
26579      * @param {Boolean} keepExisting (optional) True to keep existing selections
26580      */
26581     selectLastRow : function(keepExisting){
26582         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26583         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26584     },
26585
26586     /**
26587      * Selects the row immediately following the last selected row.
26588      * @param {Boolean} keepExisting (optional) True to keep existing selections
26589      */
26590     selectNext : function(keepExisting)
26591     {
26592             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26593             this.selectRow(this.last+1, keepExisting);
26594             this.grid.getView().focusRow(this.last);
26595         }
26596     },
26597
26598     /**
26599      * Selects the row that precedes the last selected row.
26600      * @param {Boolean} keepExisting (optional) True to keep existing selections
26601      */
26602     selectPrevious : function(keepExisting){
26603         if(this.last){
26604             this.selectRow(this.last-1, keepExisting);
26605             this.grid.getView().focusRow(this.last);
26606         }
26607     },
26608
26609     /**
26610      * Returns the selected records
26611      * @return {Array} Array of selected records
26612      */
26613     getSelections : function(){
26614         return [].concat(this.selections.items);
26615     },
26616
26617     /**
26618      * Returns the first selected record.
26619      * @return {Record}
26620      */
26621     getSelected : function(){
26622         return this.selections.itemAt(0);
26623     },
26624
26625
26626     /**
26627      * Clears all selections.
26628      */
26629     clearSelections : function(fast)
26630     {
26631         if(this.locked) {
26632             return;
26633         }
26634         if(fast !== true){
26635                 var ds = this.grid.store;
26636             var s = this.selections;
26637             s.each(function(r){
26638                 this.deselectRow(ds.indexOfId(r.id));
26639             }, this);
26640             s.clear();
26641         }else{
26642             this.selections.clear();
26643         }
26644         this.last = false;
26645     },
26646
26647
26648     /**
26649      * Selects all rows.
26650      */
26651     selectAll : function(){
26652         if(this.locked) {
26653             return;
26654         }
26655         this.selections.clear();
26656         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26657             this.selectRow(i, true);
26658         }
26659     },
26660
26661     /**
26662      * Returns True if there is a selection.
26663      * @return {Boolean}
26664      */
26665     hasSelection : function(){
26666         return this.selections.length > 0;
26667     },
26668
26669     /**
26670      * Returns True if the specified row is selected.
26671      * @param {Number/Record} record The record or index of the record to check
26672      * @return {Boolean}
26673      */
26674     isSelected : function(index){
26675             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26676         return (r && this.selections.key(r.id) ? true : false);
26677     },
26678
26679     /**
26680      * Returns True if the specified record id is selected.
26681      * @param {String} id The id of record to check
26682      * @return {Boolean}
26683      */
26684     isIdSelected : function(id){
26685         return (this.selections.key(id) ? true : false);
26686     },
26687
26688
26689     // private
26690     handleMouseDBClick : function(e, t){
26691         
26692     },
26693     // private
26694     handleMouseDown : function(e, t)
26695     {
26696             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26697         if(this.isLocked() || rowIndex < 0 ){
26698             return;
26699         };
26700         if(e.shiftKey && this.last !== false){
26701             var last = this.last;
26702             this.selectRange(last, rowIndex, e.ctrlKey);
26703             this.last = last; // reset the last
26704             t.focus();
26705     
26706         }else{
26707             var isSelected = this.isSelected(rowIndex);
26708             //Roo.log("select row:" + rowIndex);
26709             if(isSelected){
26710                 this.deselectRow(rowIndex);
26711             } else {
26712                         this.selectRow(rowIndex, true);
26713             }
26714     
26715             /*
26716                 if(e.button !== 0 && isSelected){
26717                 alert('rowIndex 2: ' + rowIndex);
26718                     view.focusRow(rowIndex);
26719                 }else if(e.ctrlKey && isSelected){
26720                     this.deselectRow(rowIndex);
26721                 }else if(!isSelected){
26722                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26723                     view.focusRow(rowIndex);
26724                 }
26725             */
26726         }
26727         this.fireEvent("afterselectionchange", this);
26728     },
26729     // private
26730     handleDragableRowClick :  function(grid, rowIndex, e) 
26731     {
26732         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26733             this.selectRow(rowIndex, false);
26734             grid.view.focusRow(rowIndex);
26735              this.fireEvent("afterselectionchange", this);
26736         }
26737     },
26738     
26739     /**
26740      * Selects multiple rows.
26741      * @param {Array} rows Array of the indexes of the row to select
26742      * @param {Boolean} keepExisting (optional) True to keep existing selections
26743      */
26744     selectRows : function(rows, keepExisting){
26745         if(!keepExisting){
26746             this.clearSelections();
26747         }
26748         for(var i = 0, len = rows.length; i < len; i++){
26749             this.selectRow(rows[i], true);
26750         }
26751     },
26752
26753     /**
26754      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26755      * @param {Number} startRow The index of the first row in the range
26756      * @param {Number} endRow The index of the last row in the range
26757      * @param {Boolean} keepExisting (optional) True to retain existing selections
26758      */
26759     selectRange : function(startRow, endRow, keepExisting){
26760         if(this.locked) {
26761             return;
26762         }
26763         if(!keepExisting){
26764             this.clearSelections();
26765         }
26766         if(startRow <= endRow){
26767             for(var i = startRow; i <= endRow; i++){
26768                 this.selectRow(i, true);
26769             }
26770         }else{
26771             for(var i = startRow; i >= endRow; i--){
26772                 this.selectRow(i, true);
26773             }
26774         }
26775     },
26776
26777     /**
26778      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26779      * @param {Number} startRow The index of the first row in the range
26780      * @param {Number} endRow The index of the last row in the range
26781      */
26782     deselectRange : function(startRow, endRow, preventViewNotify){
26783         if(this.locked) {
26784             return;
26785         }
26786         for(var i = startRow; i <= endRow; i++){
26787             this.deselectRow(i, preventViewNotify);
26788         }
26789     },
26790
26791     /**
26792      * Selects a row.
26793      * @param {Number} row The index of the row to select
26794      * @param {Boolean} keepExisting (optional) True to keep existing selections
26795      */
26796     selectRow : function(index, keepExisting, preventViewNotify)
26797     {
26798             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26799             return;
26800         }
26801         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26802             if(!keepExisting || this.singleSelect){
26803                 this.clearSelections();
26804             }
26805             
26806             var r = this.grid.store.getAt(index);
26807             //console.log('selectRow - record id :' + r.id);
26808             
26809             this.selections.add(r);
26810             this.last = this.lastActive = index;
26811             if(!preventViewNotify){
26812                 var proxy = new Roo.Element(
26813                                 this.grid.getRowDom(index)
26814                 );
26815                 proxy.addClass('bg-info info');
26816             }
26817             this.fireEvent("rowselect", this, index, r);
26818             this.fireEvent("selectionchange", this);
26819         }
26820     },
26821
26822     /**
26823      * Deselects a row.
26824      * @param {Number} row The index of the row to deselect
26825      */
26826     deselectRow : function(index, preventViewNotify)
26827     {
26828         if(this.locked) {
26829             return;
26830         }
26831         if(this.last == index){
26832             this.last = false;
26833         }
26834         if(this.lastActive == index){
26835             this.lastActive = false;
26836         }
26837         
26838         var r = this.grid.store.getAt(index);
26839         if (!r) {
26840             return;
26841         }
26842         
26843         this.selections.remove(r);
26844         //.console.log('deselectRow - record id :' + r.id);
26845         if(!preventViewNotify){
26846         
26847             var proxy = new Roo.Element(
26848                 this.grid.getRowDom(index)
26849             );
26850             proxy.removeClass('bg-info info');
26851         }
26852         this.fireEvent("rowdeselect", this, index);
26853         this.fireEvent("selectionchange", this);
26854     },
26855
26856     // private
26857     restoreLast : function(){
26858         if(this._last){
26859             this.last = this._last;
26860         }
26861     },
26862
26863     // private
26864     acceptsNav : function(row, col, cm){
26865         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26866     },
26867
26868     // private
26869     onEditorKey : function(field, e){
26870         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26871         if(k == e.TAB){
26872             e.stopEvent();
26873             ed.completeEdit();
26874             if(e.shiftKey){
26875                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26876             }else{
26877                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26878             }
26879         }else if(k == e.ENTER && !e.ctrlKey){
26880             e.stopEvent();
26881             ed.completeEdit();
26882             if(e.shiftKey){
26883                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26884             }else{
26885                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26886             }
26887         }else if(k == e.ESC){
26888             ed.cancelEdit();
26889         }
26890         if(newCell){
26891             g.startEditing(newCell[0], newCell[1]);
26892         }
26893     }
26894 });
26895 /*
26896  * Based on:
26897  * Ext JS Library 1.1.1
26898  * Copyright(c) 2006-2007, Ext JS, LLC.
26899  *
26900  * Originally Released Under LGPL - original licence link has changed is not relivant.
26901  *
26902  * Fork - LGPL
26903  * <script type="text/javascript">
26904  */
26905  
26906 /**
26907  * @class Roo.bootstrap.PagingToolbar
26908  * @extends Roo.bootstrap.NavSimplebar
26909  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26910  * @constructor
26911  * Create a new PagingToolbar
26912  * @param {Object} config The config object
26913  * @param {Roo.data.Store} store
26914  */
26915 Roo.bootstrap.PagingToolbar = function(config)
26916 {
26917     // old args format still supported... - xtype is prefered..
26918         // created from xtype...
26919     
26920     this.ds = config.dataSource;
26921     
26922     if (config.store && !this.ds) {
26923         this.store= Roo.factory(config.store, Roo.data);
26924         this.ds = this.store;
26925         this.ds.xmodule = this.xmodule || false;
26926     }
26927     
26928     this.toolbarItems = [];
26929     if (config.items) {
26930         this.toolbarItems = config.items;
26931     }
26932     
26933     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26934     
26935     this.cursor = 0;
26936     
26937     if (this.ds) { 
26938         this.bind(this.ds);
26939     }
26940     
26941     if (Roo.bootstrap.version == 4) {
26942         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26943     } else {
26944         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26945     }
26946     
26947 };
26948
26949 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26950     /**
26951      * @cfg {Roo.data.Store} dataSource
26952      * The underlying data store providing the paged data
26953      */
26954     /**
26955      * @cfg {String/HTMLElement/Element} container
26956      * container The id or element that will contain the toolbar
26957      */
26958     /**
26959      * @cfg {Boolean} displayInfo
26960      * True to display the displayMsg (defaults to false)
26961      */
26962     /**
26963      * @cfg {Number} pageSize
26964      * The number of records to display per page (defaults to 20)
26965      */
26966     pageSize: 20,
26967     /**
26968      * @cfg {String} displayMsg
26969      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26970      */
26971     displayMsg : 'Displaying {0} - {1} of {2}',
26972     /**
26973      * @cfg {String} emptyMsg
26974      * The message to display when no records are found (defaults to "No data to display")
26975      */
26976     emptyMsg : 'No data to display',
26977     /**
26978      * Customizable piece of the default paging text (defaults to "Page")
26979      * @type String
26980      */
26981     beforePageText : "Page",
26982     /**
26983      * Customizable piece of the default paging text (defaults to "of %0")
26984      * @type String
26985      */
26986     afterPageText : "of {0}",
26987     /**
26988      * Customizable piece of the default paging text (defaults to "First Page")
26989      * @type String
26990      */
26991     firstText : "First Page",
26992     /**
26993      * Customizable piece of the default paging text (defaults to "Previous Page")
26994      * @type String
26995      */
26996     prevText : "Previous Page",
26997     /**
26998      * Customizable piece of the default paging text (defaults to "Next Page")
26999      * @type String
27000      */
27001     nextText : "Next Page",
27002     /**
27003      * Customizable piece of the default paging text (defaults to "Last Page")
27004      * @type String
27005      */
27006     lastText : "Last Page",
27007     /**
27008      * Customizable piece of the default paging text (defaults to "Refresh")
27009      * @type String
27010      */
27011     refreshText : "Refresh",
27012
27013     buttons : false,
27014     // private
27015     onRender : function(ct, position) 
27016     {
27017         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27018         this.navgroup.parentId = this.id;
27019         this.navgroup.onRender(this.el, null);
27020         // add the buttons to the navgroup
27021         
27022         if(this.displayInfo){
27023             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27024             this.displayEl = this.el.select('.x-paging-info', true).first();
27025 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27026 //            this.displayEl = navel.el.select('span',true).first();
27027         }
27028         
27029         var _this = this;
27030         
27031         if(this.buttons){
27032             Roo.each(_this.buttons, function(e){ // this might need to use render????
27033                Roo.factory(e).render(_this.el);
27034             });
27035         }
27036             
27037         Roo.each(_this.toolbarItems, function(e) {
27038             _this.navgroup.addItem(e);
27039         });
27040         
27041         
27042         this.first = this.navgroup.addItem({
27043             tooltip: this.firstText,
27044             cls: "prev btn-outline-secondary",
27045             html : ' <i class="fa fa-step-backward"></i>',
27046             disabled: true,
27047             preventDefault: true,
27048             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27049         });
27050         
27051         this.prev =  this.navgroup.addItem({
27052             tooltip: this.prevText,
27053             cls: "prev btn-outline-secondary",
27054             html : ' <i class="fa fa-backward"></i>',
27055             disabled: true,
27056             preventDefault: true,
27057             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27058         });
27059     //this.addSeparator();
27060         
27061         
27062         var field = this.navgroup.addItem( {
27063             tagtype : 'span',
27064             cls : 'x-paging-position  btn-outline-secondary',
27065              disabled: true,
27066             html : this.beforePageText  +
27067                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27068                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27069          } ); //?? escaped?
27070         
27071         this.field = field.el.select('input', true).first();
27072         this.field.on("keydown", this.onPagingKeydown, this);
27073         this.field.on("focus", function(){this.dom.select();});
27074     
27075     
27076         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27077         //this.field.setHeight(18);
27078         //this.addSeparator();
27079         this.next = this.navgroup.addItem({
27080             tooltip: this.nextText,
27081             cls: "next btn-outline-secondary",
27082             html : ' <i class="fa fa-forward"></i>',
27083             disabled: true,
27084             preventDefault: true,
27085             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27086         });
27087         this.last = this.navgroup.addItem({
27088             tooltip: this.lastText,
27089             html : ' <i class="fa fa-step-forward"></i>',
27090             cls: "next btn-outline-secondary",
27091             disabled: true,
27092             preventDefault: true,
27093             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27094         });
27095     //this.addSeparator();
27096         this.loading = this.navgroup.addItem({
27097             tooltip: this.refreshText,
27098             cls: "btn-outline-secondary",
27099             html : ' <i class="fa fa-refresh"></i>',
27100             preventDefault: true,
27101             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27102         });
27103         
27104     },
27105
27106     // private
27107     updateInfo : function(){
27108         if(this.displayEl){
27109             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27110             var msg = count == 0 ?
27111                 this.emptyMsg :
27112                 String.format(
27113                     this.displayMsg,
27114                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27115                 );
27116             this.displayEl.update(msg);
27117         }
27118     },
27119
27120     // private
27121     onLoad : function(ds, r, o)
27122     {
27123         this.cursor = o.params.start ? o.params.start : 0;
27124         
27125         var d = this.getPageData(),
27126             ap = d.activePage,
27127             ps = d.pages;
27128         
27129         
27130         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27131         this.field.dom.value = ap;
27132         this.first.setDisabled(ap == 1);
27133         this.prev.setDisabled(ap == 1);
27134         this.next.setDisabled(ap == ps);
27135         this.last.setDisabled(ap == ps);
27136         this.loading.enable();
27137         this.updateInfo();
27138     },
27139
27140     // private
27141     getPageData : function(){
27142         var total = this.ds.getTotalCount();
27143         return {
27144             total : total,
27145             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27146             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27147         };
27148     },
27149
27150     // private
27151     onLoadError : function(){
27152         this.loading.enable();
27153     },
27154
27155     // private
27156     onPagingKeydown : function(e){
27157         var k = e.getKey();
27158         var d = this.getPageData();
27159         if(k == e.RETURN){
27160             var v = this.field.dom.value, pageNum;
27161             if(!v || isNaN(pageNum = parseInt(v, 10))){
27162                 this.field.dom.value = d.activePage;
27163                 return;
27164             }
27165             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27166             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27167             e.stopEvent();
27168         }
27169         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))
27170         {
27171           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27172           this.field.dom.value = pageNum;
27173           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27174           e.stopEvent();
27175         }
27176         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27177         {
27178           var v = this.field.dom.value, pageNum; 
27179           var increment = (e.shiftKey) ? 10 : 1;
27180           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27181                 increment *= -1;
27182           }
27183           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27184             this.field.dom.value = d.activePage;
27185             return;
27186           }
27187           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27188           {
27189             this.field.dom.value = parseInt(v, 10) + increment;
27190             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27191             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27192           }
27193           e.stopEvent();
27194         }
27195     },
27196
27197     // private
27198     beforeLoad : function(){
27199         if(this.loading){
27200             this.loading.disable();
27201         }
27202     },
27203
27204     // private
27205     onClick : function(which){
27206         
27207         var ds = this.ds;
27208         if (!ds) {
27209             return;
27210         }
27211         
27212         switch(which){
27213             case "first":
27214                 ds.load({params:{start: 0, limit: this.pageSize}});
27215             break;
27216             case "prev":
27217                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27218             break;
27219             case "next":
27220                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27221             break;
27222             case "last":
27223                 var total = ds.getTotalCount();
27224                 var extra = total % this.pageSize;
27225                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27226                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27227             break;
27228             case "refresh":
27229                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27230             break;
27231         }
27232     },
27233
27234     /**
27235      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27236      * @param {Roo.data.Store} store The data store to unbind
27237      */
27238     unbind : function(ds){
27239         ds.un("beforeload", this.beforeLoad, this);
27240         ds.un("load", this.onLoad, this);
27241         ds.un("loadexception", this.onLoadError, this);
27242         ds.un("remove", this.updateInfo, this);
27243         ds.un("add", this.updateInfo, this);
27244         this.ds = undefined;
27245     },
27246
27247     /**
27248      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27249      * @param {Roo.data.Store} store The data store to bind
27250      */
27251     bind : function(ds){
27252         ds.on("beforeload", this.beforeLoad, this);
27253         ds.on("load", this.onLoad, this);
27254         ds.on("loadexception", this.onLoadError, this);
27255         ds.on("remove", this.updateInfo, this);
27256         ds.on("add", this.updateInfo, this);
27257         this.ds = ds;
27258     }
27259 });/*
27260  * - LGPL
27261  *
27262  * element
27263  * 
27264  */
27265
27266 /**
27267  * @class Roo.bootstrap.MessageBar
27268  * @extends Roo.bootstrap.Component
27269  * Bootstrap MessageBar class
27270  * @cfg {String} html contents of the MessageBar
27271  * @cfg {String} weight (info | success | warning | danger) default info
27272  * @cfg {String} beforeClass insert the bar before the given class
27273  * @cfg {Boolean} closable (true | false) default false
27274  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27275  * 
27276  * @constructor
27277  * Create a new Element
27278  * @param {Object} config The config object
27279  */
27280
27281 Roo.bootstrap.MessageBar = function(config){
27282     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27283 };
27284
27285 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27286     
27287     html: '',
27288     weight: 'info',
27289     closable: false,
27290     fixed: false,
27291     beforeClass: 'bootstrap-sticky-wrap',
27292     
27293     getAutoCreate : function(){
27294         
27295         var cfg = {
27296             tag: 'div',
27297             cls: 'alert alert-dismissable alert-' + this.weight,
27298             cn: [
27299                 {
27300                     tag: 'span',
27301                     cls: 'message',
27302                     html: this.html || ''
27303                 }
27304             ]
27305         };
27306         
27307         if(this.fixed){
27308             cfg.cls += ' alert-messages-fixed';
27309         }
27310         
27311         if(this.closable){
27312             cfg.cn.push({
27313                 tag: 'button',
27314                 cls: 'close',
27315                 html: 'x'
27316             });
27317         }
27318         
27319         return cfg;
27320     },
27321     
27322     onRender : function(ct, position)
27323     {
27324         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27325         
27326         if(!this.el){
27327             var cfg = Roo.apply({},  this.getAutoCreate());
27328             cfg.id = Roo.id();
27329             
27330             if (this.cls) {
27331                 cfg.cls += ' ' + this.cls;
27332             }
27333             if (this.style) {
27334                 cfg.style = this.style;
27335             }
27336             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27337             
27338             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27339         }
27340         
27341         this.el.select('>button.close').on('click', this.hide, this);
27342         
27343     },
27344     
27345     show : function()
27346     {
27347         if (!this.rendered) {
27348             this.render();
27349         }
27350         
27351         this.el.show();
27352         
27353         this.fireEvent('show', this);
27354         
27355     },
27356     
27357     hide : function()
27358     {
27359         if (!this.rendered) {
27360             this.render();
27361         }
27362         
27363         this.el.hide();
27364         
27365         this.fireEvent('hide', this);
27366     },
27367     
27368     update : function()
27369     {
27370 //        var e = this.el.dom.firstChild;
27371 //        
27372 //        if(this.closable){
27373 //            e = e.nextSibling;
27374 //        }
27375 //        
27376 //        e.data = this.html || '';
27377
27378         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27379     }
27380    
27381 });
27382
27383  
27384
27385      /*
27386  * - LGPL
27387  *
27388  * Graph
27389  * 
27390  */
27391
27392
27393 /**
27394  * @class Roo.bootstrap.Graph
27395  * @extends Roo.bootstrap.Component
27396  * Bootstrap Graph class
27397 > Prameters
27398  -sm {number} sm 4
27399  -md {number} md 5
27400  @cfg {String} graphtype  bar | vbar | pie
27401  @cfg {number} g_x coodinator | centre x (pie)
27402  @cfg {number} g_y coodinator | centre y (pie)
27403  @cfg {number} g_r radius (pie)
27404  @cfg {number} g_height height of the chart (respected by all elements in the set)
27405  @cfg {number} g_width width of the chart (respected by all elements in the set)
27406  @cfg {Object} title The title of the chart
27407     
27408  -{Array}  values
27409  -opts (object) options for the chart 
27410      o {
27411      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27412      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27413      o vgutter (number)
27414      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.
27415      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27416      o to
27417      o stretch (boolean)
27418      o }
27419  -opts (object) options for the pie
27420      o{
27421      o cut
27422      o startAngle (number)
27423      o endAngle (number)
27424      } 
27425  *
27426  * @constructor
27427  * Create a new Input
27428  * @param {Object} config The config object
27429  */
27430
27431 Roo.bootstrap.Graph = function(config){
27432     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27433     
27434     this.addEvents({
27435         // img events
27436         /**
27437          * @event click
27438          * The img click event for the img.
27439          * @param {Roo.EventObject} e
27440          */
27441         "click" : true
27442     });
27443 };
27444
27445 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27446     
27447     sm: 4,
27448     md: 5,
27449     graphtype: 'bar',
27450     g_height: 250,
27451     g_width: 400,
27452     g_x: 50,
27453     g_y: 50,
27454     g_r: 30,
27455     opts:{
27456         //g_colors: this.colors,
27457         g_type: 'soft',
27458         g_gutter: '20%'
27459
27460     },
27461     title : false,
27462
27463     getAutoCreate : function(){
27464         
27465         var cfg = {
27466             tag: 'div',
27467             html : null
27468         };
27469         
27470         
27471         return  cfg;
27472     },
27473
27474     onRender : function(ct,position){
27475         
27476         
27477         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27478         
27479         if (typeof(Raphael) == 'undefined') {
27480             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27481             return;
27482         }
27483         
27484         this.raphael = Raphael(this.el.dom);
27485         
27486                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27487                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27488                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27489                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27490                 /*
27491                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27492                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27493                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27494                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27495                 
27496                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27497                 r.barchart(330, 10, 300, 220, data1);
27498                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27499                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27500                 */
27501                 
27502                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27503                 // r.barchart(30, 30, 560, 250,  xdata, {
27504                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27505                 //     axis : "0 0 1 1",
27506                 //     axisxlabels :  xdata
27507                 //     //yvalues : cols,
27508                    
27509                 // });
27510 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27511 //        
27512 //        this.load(null,xdata,{
27513 //                axis : "0 0 1 1",
27514 //                axisxlabels :  xdata
27515 //                });
27516
27517     },
27518
27519     load : function(graphtype,xdata,opts)
27520     {
27521         this.raphael.clear();
27522         if(!graphtype) {
27523             graphtype = this.graphtype;
27524         }
27525         if(!opts){
27526             opts = this.opts;
27527         }
27528         var r = this.raphael,
27529             fin = function () {
27530                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27531             },
27532             fout = function () {
27533                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27534             },
27535             pfin = function() {
27536                 this.sector.stop();
27537                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27538
27539                 if (this.label) {
27540                     this.label[0].stop();
27541                     this.label[0].attr({ r: 7.5 });
27542                     this.label[1].attr({ "font-weight": 800 });
27543                 }
27544             },
27545             pfout = function() {
27546                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27547
27548                 if (this.label) {
27549                     this.label[0].animate({ r: 5 }, 500, "bounce");
27550                     this.label[1].attr({ "font-weight": 400 });
27551                 }
27552             };
27553
27554         switch(graphtype){
27555             case 'bar':
27556                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27557                 break;
27558             case 'hbar':
27559                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27560                 break;
27561             case 'pie':
27562 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27563 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27564 //            
27565                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27566                 
27567                 break;
27568
27569         }
27570         
27571         if(this.title){
27572             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27573         }
27574         
27575     },
27576     
27577     setTitle: function(o)
27578     {
27579         this.title = o;
27580     },
27581     
27582     initEvents: function() {
27583         
27584         if(!this.href){
27585             this.el.on('click', this.onClick, this);
27586         }
27587     },
27588     
27589     onClick : function(e)
27590     {
27591         Roo.log('img onclick');
27592         this.fireEvent('click', this, e);
27593     }
27594    
27595 });
27596
27597  
27598 /*
27599  * - LGPL
27600  *
27601  * numberBox
27602  * 
27603  */
27604 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27605
27606 /**
27607  * @class Roo.bootstrap.dash.NumberBox
27608  * @extends Roo.bootstrap.Component
27609  * Bootstrap NumberBox class
27610  * @cfg {String} headline Box headline
27611  * @cfg {String} content Box content
27612  * @cfg {String} icon Box icon
27613  * @cfg {String} footer Footer text
27614  * @cfg {String} fhref Footer href
27615  * 
27616  * @constructor
27617  * Create a new NumberBox
27618  * @param {Object} config The config object
27619  */
27620
27621
27622 Roo.bootstrap.dash.NumberBox = function(config){
27623     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27624     
27625 };
27626
27627 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27628     
27629     headline : '',
27630     content : '',
27631     icon : '',
27632     footer : '',
27633     fhref : '',
27634     ficon : '',
27635     
27636     getAutoCreate : function(){
27637         
27638         var cfg = {
27639             tag : 'div',
27640             cls : 'small-box ',
27641             cn : [
27642                 {
27643                     tag : 'div',
27644                     cls : 'inner',
27645                     cn :[
27646                         {
27647                             tag : 'h3',
27648                             cls : 'roo-headline',
27649                             html : this.headline
27650                         },
27651                         {
27652                             tag : 'p',
27653                             cls : 'roo-content',
27654                             html : this.content
27655                         }
27656                     ]
27657                 }
27658             ]
27659         };
27660         
27661         if(this.icon){
27662             cfg.cn.push({
27663                 tag : 'div',
27664                 cls : 'icon',
27665                 cn :[
27666                     {
27667                         tag : 'i',
27668                         cls : 'ion ' + this.icon
27669                     }
27670                 ]
27671             });
27672         }
27673         
27674         if(this.footer){
27675             var footer = {
27676                 tag : 'a',
27677                 cls : 'small-box-footer',
27678                 href : this.fhref || '#',
27679                 html : this.footer
27680             };
27681             
27682             cfg.cn.push(footer);
27683             
27684         }
27685         
27686         return  cfg;
27687     },
27688
27689     onRender : function(ct,position){
27690         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27691
27692
27693        
27694                 
27695     },
27696
27697     setHeadline: function (value)
27698     {
27699         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27700     },
27701     
27702     setFooter: function (value, href)
27703     {
27704         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27705         
27706         if(href){
27707             this.el.select('a.small-box-footer',true).first().attr('href', href);
27708         }
27709         
27710     },
27711
27712     setContent: function (value)
27713     {
27714         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27715     },
27716
27717     initEvents: function() 
27718     {   
27719         
27720     }
27721     
27722 });
27723
27724  
27725 /*
27726  * - LGPL
27727  *
27728  * TabBox
27729  * 
27730  */
27731 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27732
27733 /**
27734  * @class Roo.bootstrap.dash.TabBox
27735  * @extends Roo.bootstrap.Component
27736  * Bootstrap TabBox class
27737  * @cfg {String} title Title of the TabBox
27738  * @cfg {String} icon Icon of the TabBox
27739  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27740  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27741  * 
27742  * @constructor
27743  * Create a new TabBox
27744  * @param {Object} config The config object
27745  */
27746
27747
27748 Roo.bootstrap.dash.TabBox = function(config){
27749     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27750     this.addEvents({
27751         // raw events
27752         /**
27753          * @event addpane
27754          * When a pane is added
27755          * @param {Roo.bootstrap.dash.TabPane} pane
27756          */
27757         "addpane" : true,
27758         /**
27759          * @event activatepane
27760          * When a pane is activated
27761          * @param {Roo.bootstrap.dash.TabPane} pane
27762          */
27763         "activatepane" : true
27764         
27765          
27766     });
27767     
27768     this.panes = [];
27769 };
27770
27771 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27772
27773     title : '',
27774     icon : false,
27775     showtabs : true,
27776     tabScrollable : false,
27777     
27778     getChildContainer : function()
27779     {
27780         return this.el.select('.tab-content', true).first();
27781     },
27782     
27783     getAutoCreate : function(){
27784         
27785         var header = {
27786             tag: 'li',
27787             cls: 'pull-left header',
27788             html: this.title,
27789             cn : []
27790         };
27791         
27792         if(this.icon){
27793             header.cn.push({
27794                 tag: 'i',
27795                 cls: 'fa ' + this.icon
27796             });
27797         }
27798         
27799         var h = {
27800             tag: 'ul',
27801             cls: 'nav nav-tabs pull-right',
27802             cn: [
27803                 header
27804             ]
27805         };
27806         
27807         if(this.tabScrollable){
27808             h = {
27809                 tag: 'div',
27810                 cls: 'tab-header',
27811                 cn: [
27812                     {
27813                         tag: 'ul',
27814                         cls: 'nav nav-tabs pull-right',
27815                         cn: [
27816                             header
27817                         ]
27818                     }
27819                 ]
27820             };
27821         }
27822         
27823         var cfg = {
27824             tag: 'div',
27825             cls: 'nav-tabs-custom',
27826             cn: [
27827                 h,
27828                 {
27829                     tag: 'div',
27830                     cls: 'tab-content no-padding',
27831                     cn: []
27832                 }
27833             ]
27834         };
27835
27836         return  cfg;
27837     },
27838     initEvents : function()
27839     {
27840         //Roo.log('add add pane handler');
27841         this.on('addpane', this.onAddPane, this);
27842     },
27843      /**
27844      * Updates the box title
27845      * @param {String} html to set the title to.
27846      */
27847     setTitle : function(value)
27848     {
27849         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27850     },
27851     onAddPane : function(pane)
27852     {
27853         this.panes.push(pane);
27854         //Roo.log('addpane');
27855         //Roo.log(pane);
27856         // tabs are rendere left to right..
27857         if(!this.showtabs){
27858             return;
27859         }
27860         
27861         var ctr = this.el.select('.nav-tabs', true).first();
27862          
27863          
27864         var existing = ctr.select('.nav-tab',true);
27865         var qty = existing.getCount();;
27866         
27867         
27868         var tab = ctr.createChild({
27869             tag : 'li',
27870             cls : 'nav-tab' + (qty ? '' : ' active'),
27871             cn : [
27872                 {
27873                     tag : 'a',
27874                     href:'#',
27875                     html : pane.title
27876                 }
27877             ]
27878         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27879         pane.tab = tab;
27880         
27881         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27882         if (!qty) {
27883             pane.el.addClass('active');
27884         }
27885         
27886                 
27887     },
27888     onTabClick : function(ev,un,ob,pane)
27889     {
27890         //Roo.log('tab - prev default');
27891         ev.preventDefault();
27892         
27893         
27894         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27895         pane.tab.addClass('active');
27896         //Roo.log(pane.title);
27897         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27898         // technically we should have a deactivate event.. but maybe add later.
27899         // and it should not de-activate the selected tab...
27900         this.fireEvent('activatepane', pane);
27901         pane.el.addClass('active');
27902         pane.fireEvent('activate');
27903         
27904         
27905     },
27906     
27907     getActivePane : function()
27908     {
27909         var r = false;
27910         Roo.each(this.panes, function(p) {
27911             if(p.el.hasClass('active')){
27912                 r = p;
27913                 return false;
27914             }
27915             
27916             return;
27917         });
27918         
27919         return r;
27920     }
27921     
27922     
27923 });
27924
27925  
27926 /*
27927  * - LGPL
27928  *
27929  * Tab pane
27930  * 
27931  */
27932 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27933 /**
27934  * @class Roo.bootstrap.TabPane
27935  * @extends Roo.bootstrap.Component
27936  * Bootstrap TabPane class
27937  * @cfg {Boolean} active (false | true) Default false
27938  * @cfg {String} title title of panel
27939
27940  * 
27941  * @constructor
27942  * Create a new TabPane
27943  * @param {Object} config The config object
27944  */
27945
27946 Roo.bootstrap.dash.TabPane = function(config){
27947     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27948     
27949     this.addEvents({
27950         // raw events
27951         /**
27952          * @event activate
27953          * When a pane is activated
27954          * @param {Roo.bootstrap.dash.TabPane} pane
27955          */
27956         "activate" : true
27957          
27958     });
27959 };
27960
27961 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27962     
27963     active : false,
27964     title : '',
27965     
27966     // the tabBox that this is attached to.
27967     tab : false,
27968      
27969     getAutoCreate : function() 
27970     {
27971         var cfg = {
27972             tag: 'div',
27973             cls: 'tab-pane'
27974         };
27975         
27976         if(this.active){
27977             cfg.cls += ' active';
27978         }
27979         
27980         return cfg;
27981     },
27982     initEvents  : function()
27983     {
27984         //Roo.log('trigger add pane handler');
27985         this.parent().fireEvent('addpane', this)
27986     },
27987     
27988      /**
27989      * Updates the tab title 
27990      * @param {String} html to set the title to.
27991      */
27992     setTitle: function(str)
27993     {
27994         if (!this.tab) {
27995             return;
27996         }
27997         this.title = str;
27998         this.tab.select('a', true).first().dom.innerHTML = str;
27999         
28000     }
28001     
28002     
28003     
28004 });
28005
28006  
28007
28008
28009  /*
28010  * - LGPL
28011  *
28012  * menu
28013  * 
28014  */
28015 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28016
28017 /**
28018  * @class Roo.bootstrap.menu.Menu
28019  * @extends Roo.bootstrap.Component
28020  * Bootstrap Menu class - container for Menu
28021  * @cfg {String} html Text of the menu
28022  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28023  * @cfg {String} icon Font awesome icon
28024  * @cfg {String} pos Menu align to (top | bottom) default bottom
28025  * 
28026  * 
28027  * @constructor
28028  * Create a new Menu
28029  * @param {Object} config The config object
28030  */
28031
28032
28033 Roo.bootstrap.menu.Menu = function(config){
28034     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28035     
28036     this.addEvents({
28037         /**
28038          * @event beforeshow
28039          * Fires before this menu is displayed
28040          * @param {Roo.bootstrap.menu.Menu} this
28041          */
28042         beforeshow : true,
28043         /**
28044          * @event beforehide
28045          * Fires before this menu is hidden
28046          * @param {Roo.bootstrap.menu.Menu} this
28047          */
28048         beforehide : true,
28049         /**
28050          * @event show
28051          * Fires after this menu is displayed
28052          * @param {Roo.bootstrap.menu.Menu} this
28053          */
28054         show : true,
28055         /**
28056          * @event hide
28057          * Fires after this menu is hidden
28058          * @param {Roo.bootstrap.menu.Menu} this
28059          */
28060         hide : true,
28061         /**
28062          * @event click
28063          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28064          * @param {Roo.bootstrap.menu.Menu} this
28065          * @param {Roo.EventObject} e
28066          */
28067         click : true
28068     });
28069     
28070 };
28071
28072 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28073     
28074     submenu : false,
28075     html : '',
28076     weight : 'default',
28077     icon : false,
28078     pos : 'bottom',
28079     
28080     
28081     getChildContainer : function() {
28082         if(this.isSubMenu){
28083             return this.el;
28084         }
28085         
28086         return this.el.select('ul.dropdown-menu', true).first();  
28087     },
28088     
28089     getAutoCreate : function()
28090     {
28091         var text = [
28092             {
28093                 tag : 'span',
28094                 cls : 'roo-menu-text',
28095                 html : this.html
28096             }
28097         ];
28098         
28099         if(this.icon){
28100             text.unshift({
28101                 tag : 'i',
28102                 cls : 'fa ' + this.icon
28103             })
28104         }
28105         
28106         
28107         var cfg = {
28108             tag : 'div',
28109             cls : 'btn-group',
28110             cn : [
28111                 {
28112                     tag : 'button',
28113                     cls : 'dropdown-button btn btn-' + this.weight,
28114                     cn : text
28115                 },
28116                 {
28117                     tag : 'button',
28118                     cls : 'dropdown-toggle btn btn-' + this.weight,
28119                     cn : [
28120                         {
28121                             tag : 'span',
28122                             cls : 'caret'
28123                         }
28124                     ]
28125                 },
28126                 {
28127                     tag : 'ul',
28128                     cls : 'dropdown-menu'
28129                 }
28130             ]
28131             
28132         };
28133         
28134         if(this.pos == 'top'){
28135             cfg.cls += ' dropup';
28136         }
28137         
28138         if(this.isSubMenu){
28139             cfg = {
28140                 tag : 'ul',
28141                 cls : 'dropdown-menu'
28142             }
28143         }
28144         
28145         return cfg;
28146     },
28147     
28148     onRender : function(ct, position)
28149     {
28150         this.isSubMenu = ct.hasClass('dropdown-submenu');
28151         
28152         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28153     },
28154     
28155     initEvents : function() 
28156     {
28157         if(this.isSubMenu){
28158             return;
28159         }
28160         
28161         this.hidden = true;
28162         
28163         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28164         this.triggerEl.on('click', this.onTriggerPress, this);
28165         
28166         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28167         this.buttonEl.on('click', this.onClick, this);
28168         
28169     },
28170     
28171     list : function()
28172     {
28173         if(this.isSubMenu){
28174             return this.el;
28175         }
28176         
28177         return this.el.select('ul.dropdown-menu', true).first();
28178     },
28179     
28180     onClick : function(e)
28181     {
28182         this.fireEvent("click", this, e);
28183     },
28184     
28185     onTriggerPress  : function(e)
28186     {   
28187         if (this.isVisible()) {
28188             this.hide();
28189         } else {
28190             this.show();
28191         }
28192     },
28193     
28194     isVisible : function(){
28195         return !this.hidden;
28196     },
28197     
28198     show : function()
28199     {
28200         this.fireEvent("beforeshow", this);
28201         
28202         this.hidden = false;
28203         this.el.addClass('open');
28204         
28205         Roo.get(document).on("mouseup", this.onMouseUp, this);
28206         
28207         this.fireEvent("show", this);
28208         
28209         
28210     },
28211     
28212     hide : function()
28213     {
28214         this.fireEvent("beforehide", this);
28215         
28216         this.hidden = true;
28217         this.el.removeClass('open');
28218         
28219         Roo.get(document).un("mouseup", this.onMouseUp);
28220         
28221         this.fireEvent("hide", this);
28222     },
28223     
28224     onMouseUp : function()
28225     {
28226         this.hide();
28227     }
28228     
28229 });
28230
28231  
28232  /*
28233  * - LGPL
28234  *
28235  * menu item
28236  * 
28237  */
28238 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28239
28240 /**
28241  * @class Roo.bootstrap.menu.Item
28242  * @extends Roo.bootstrap.Component
28243  * Bootstrap MenuItem class
28244  * @cfg {Boolean} submenu (true | false) default false
28245  * @cfg {String} html text of the item
28246  * @cfg {String} href the link
28247  * @cfg {Boolean} disable (true | false) default false
28248  * @cfg {Boolean} preventDefault (true | false) default true
28249  * @cfg {String} icon Font awesome icon
28250  * @cfg {String} pos Submenu align to (left | right) default right 
28251  * 
28252  * 
28253  * @constructor
28254  * Create a new Item
28255  * @param {Object} config The config object
28256  */
28257
28258
28259 Roo.bootstrap.menu.Item = function(config){
28260     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28261     this.addEvents({
28262         /**
28263          * @event mouseover
28264          * Fires when the mouse is hovering over this menu
28265          * @param {Roo.bootstrap.menu.Item} this
28266          * @param {Roo.EventObject} e
28267          */
28268         mouseover : true,
28269         /**
28270          * @event mouseout
28271          * Fires when the mouse exits this menu
28272          * @param {Roo.bootstrap.menu.Item} this
28273          * @param {Roo.EventObject} e
28274          */
28275         mouseout : true,
28276         // raw events
28277         /**
28278          * @event click
28279          * The raw click event for the entire grid.
28280          * @param {Roo.EventObject} e
28281          */
28282         click : true
28283     });
28284 };
28285
28286 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28287     
28288     submenu : false,
28289     href : '',
28290     html : '',
28291     preventDefault: true,
28292     disable : false,
28293     icon : false,
28294     pos : 'right',
28295     
28296     getAutoCreate : function()
28297     {
28298         var text = [
28299             {
28300                 tag : 'span',
28301                 cls : 'roo-menu-item-text',
28302                 html : this.html
28303             }
28304         ];
28305         
28306         if(this.icon){
28307             text.unshift({
28308                 tag : 'i',
28309                 cls : 'fa ' + this.icon
28310             })
28311         }
28312         
28313         var cfg = {
28314             tag : 'li',
28315             cn : [
28316                 {
28317                     tag : 'a',
28318                     href : this.href || '#',
28319                     cn : text
28320                 }
28321             ]
28322         };
28323         
28324         if(this.disable){
28325             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28326         }
28327         
28328         if(this.submenu){
28329             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28330             
28331             if(this.pos == 'left'){
28332                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28333             }
28334         }
28335         
28336         return cfg;
28337     },
28338     
28339     initEvents : function() 
28340     {
28341         this.el.on('mouseover', this.onMouseOver, this);
28342         this.el.on('mouseout', this.onMouseOut, this);
28343         
28344         this.el.select('a', true).first().on('click', this.onClick, this);
28345         
28346     },
28347     
28348     onClick : function(e)
28349     {
28350         if(this.preventDefault){
28351             e.preventDefault();
28352         }
28353         
28354         this.fireEvent("click", this, e);
28355     },
28356     
28357     onMouseOver : function(e)
28358     {
28359         if(this.submenu && this.pos == 'left'){
28360             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28361         }
28362         
28363         this.fireEvent("mouseover", this, e);
28364     },
28365     
28366     onMouseOut : function(e)
28367     {
28368         this.fireEvent("mouseout", this, e);
28369     }
28370 });
28371
28372  
28373
28374  /*
28375  * - LGPL
28376  *
28377  * menu separator
28378  * 
28379  */
28380 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28381
28382 /**
28383  * @class Roo.bootstrap.menu.Separator
28384  * @extends Roo.bootstrap.Component
28385  * Bootstrap Separator class
28386  * 
28387  * @constructor
28388  * Create a new Separator
28389  * @param {Object} config The config object
28390  */
28391
28392
28393 Roo.bootstrap.menu.Separator = function(config){
28394     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28395 };
28396
28397 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28398     
28399     getAutoCreate : function(){
28400         var cfg = {
28401             tag : 'li',
28402             cls: 'divider'
28403         };
28404         
28405         return cfg;
28406     }
28407    
28408 });
28409
28410  
28411
28412  /*
28413  * - LGPL
28414  *
28415  * Tooltip
28416  * 
28417  */
28418
28419 /**
28420  * @class Roo.bootstrap.Tooltip
28421  * Bootstrap Tooltip class
28422  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28423  * to determine which dom element triggers the tooltip.
28424  * 
28425  * It needs to add support for additional attributes like tooltip-position
28426  * 
28427  * @constructor
28428  * Create a new Toolti
28429  * @param {Object} config The config object
28430  */
28431
28432 Roo.bootstrap.Tooltip = function(config){
28433     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28434     
28435     this.alignment = Roo.bootstrap.Tooltip.alignment;
28436     
28437     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28438         this.alignment = config.alignment;
28439     }
28440     
28441 };
28442
28443 Roo.apply(Roo.bootstrap.Tooltip, {
28444     /**
28445      * @function init initialize tooltip monitoring.
28446      * @static
28447      */
28448     currentEl : false,
28449     currentTip : false,
28450     currentRegion : false,
28451     
28452     //  init : delay?
28453     
28454     init : function()
28455     {
28456         Roo.get(document).on('mouseover', this.enter ,this);
28457         Roo.get(document).on('mouseout', this.leave, this);
28458          
28459         
28460         this.currentTip = new Roo.bootstrap.Tooltip();
28461     },
28462     
28463     enter : function(ev)
28464     {
28465         var dom = ev.getTarget();
28466         
28467         //Roo.log(['enter',dom]);
28468         var el = Roo.fly(dom);
28469         if (this.currentEl) {
28470             //Roo.log(dom);
28471             //Roo.log(this.currentEl);
28472             //Roo.log(this.currentEl.contains(dom));
28473             if (this.currentEl == el) {
28474                 return;
28475             }
28476             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28477                 return;
28478             }
28479
28480         }
28481         
28482         if (this.currentTip.el) {
28483             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28484         }    
28485         //Roo.log(ev);
28486         
28487         if(!el || el.dom == document){
28488             return;
28489         }
28490         
28491         var bindEl = el;
28492         
28493         // you can not look for children, as if el is the body.. then everythign is the child..
28494         if (!el.attr('tooltip')) { //
28495             if (!el.select("[tooltip]").elements.length) {
28496                 return;
28497             }
28498             // is the mouse over this child...?
28499             bindEl = el.select("[tooltip]").first();
28500             var xy = ev.getXY();
28501             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28502                 //Roo.log("not in region.");
28503                 return;
28504             }
28505             //Roo.log("child element over..");
28506             
28507         }
28508         this.currentEl = bindEl;
28509         this.currentTip.bind(bindEl);
28510         this.currentRegion = Roo.lib.Region.getRegion(dom);
28511         this.currentTip.enter();
28512         
28513     },
28514     leave : function(ev)
28515     {
28516         var dom = ev.getTarget();
28517         //Roo.log(['leave',dom]);
28518         if (!this.currentEl) {
28519             return;
28520         }
28521         
28522         
28523         if (dom != this.currentEl.dom) {
28524             return;
28525         }
28526         var xy = ev.getXY();
28527         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28528             return;
28529         }
28530         // only activate leave if mouse cursor is outside... bounding box..
28531         
28532         
28533         
28534         
28535         if (this.currentTip) {
28536             this.currentTip.leave();
28537         }
28538         //Roo.log('clear currentEl');
28539         this.currentEl = false;
28540         
28541         
28542     },
28543     alignment : {
28544         'left' : ['r-l', [-2,0], 'right'],
28545         'right' : ['l-r', [2,0], 'left'],
28546         'bottom' : ['t-b', [0,2], 'top'],
28547         'top' : [ 'b-t', [0,-2], 'bottom']
28548     }
28549     
28550 });
28551
28552
28553 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28554     
28555     
28556     bindEl : false,
28557     
28558     delay : null, // can be { show : 300 , hide: 500}
28559     
28560     timeout : null,
28561     
28562     hoverState : null, //???
28563     
28564     placement : 'bottom', 
28565     
28566     alignment : false,
28567     
28568     getAutoCreate : function(){
28569     
28570         var cfg = {
28571            cls : 'tooltip',   
28572            role : 'tooltip',
28573            cn : [
28574                 {
28575                     cls : 'tooltip-arrow arrow'
28576                 },
28577                 {
28578                     cls : 'tooltip-inner'
28579                 }
28580            ]
28581         };
28582         
28583         return cfg;
28584     },
28585     bind : function(el)
28586     {
28587         this.bindEl = el;
28588     },
28589     
28590     initEvents : function()
28591     {
28592         this.arrowEl = this.el.select('.arrow', true).first();
28593         this.innerEl = this.el.select('.tooltip-inner', true).first();
28594     },
28595     
28596     enter : function () {
28597        
28598         if (this.timeout != null) {
28599             clearTimeout(this.timeout);
28600         }
28601         
28602         this.hoverState = 'in';
28603          //Roo.log("enter - show");
28604         if (!this.delay || !this.delay.show) {
28605             this.show();
28606             return;
28607         }
28608         var _t = this;
28609         this.timeout = setTimeout(function () {
28610             if (_t.hoverState == 'in') {
28611                 _t.show();
28612             }
28613         }, this.delay.show);
28614     },
28615     leave : function()
28616     {
28617         clearTimeout(this.timeout);
28618     
28619         this.hoverState = 'out';
28620          if (!this.delay || !this.delay.hide) {
28621             this.hide();
28622             return;
28623         }
28624        
28625         var _t = this;
28626         this.timeout = setTimeout(function () {
28627             //Roo.log("leave - timeout");
28628             
28629             if (_t.hoverState == 'out') {
28630                 _t.hide();
28631                 Roo.bootstrap.Tooltip.currentEl = false;
28632             }
28633         }, delay);
28634     },
28635     
28636     show : function (msg)
28637     {
28638         if (!this.el) {
28639             this.render(document.body);
28640         }
28641         // set content.
28642         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28643         
28644         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28645         
28646         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28647         
28648         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28649                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28650         
28651         var placement = typeof this.placement == 'function' ?
28652             this.placement.call(this, this.el, on_el) :
28653             this.placement;
28654             
28655         var autoToken = /\s?auto?\s?/i;
28656         var autoPlace = autoToken.test(placement);
28657         if (autoPlace) {
28658             placement = placement.replace(autoToken, '') || 'top';
28659         }
28660         
28661         //this.el.detach()
28662         //this.el.setXY([0,0]);
28663         this.el.show();
28664         //this.el.dom.style.display='block';
28665         
28666         //this.el.appendTo(on_el);
28667         
28668         var p = this.getPosition();
28669         var box = this.el.getBox();
28670         
28671         if (autoPlace) {
28672             // fixme..
28673         }
28674         
28675         var align = this.alignment[placement];
28676         
28677         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28678         
28679         if(placement == 'top' || placement == 'bottom'){
28680             if(xy[0] < 0){
28681                 placement = 'right';
28682             }
28683             
28684             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28685                 placement = 'left';
28686             }
28687             
28688             var scroll = Roo.select('body', true).first().getScroll();
28689             
28690             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28691                 placement = 'top';
28692             }
28693             
28694             align = this.alignment[placement];
28695             
28696             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28697             
28698         }
28699         
28700         this.el.alignTo(this.bindEl, align[0],align[1]);
28701         //var arrow = this.el.select('.arrow',true).first();
28702         //arrow.set(align[2], 
28703         
28704         this.el.addClass(placement);
28705         this.el.addClass("bs-tooltip-"+ placement);
28706         
28707         this.el.addClass('in fade show');
28708         
28709         this.hoverState = null;
28710         
28711         if (this.el.hasClass('fade')) {
28712             // fade it?
28713         }
28714         
28715         
28716         
28717         
28718         
28719     },
28720     hide : function()
28721     {
28722          
28723         if (!this.el) {
28724             return;
28725         }
28726         //this.el.setXY([0,0]);
28727         this.el.removeClass(['show', 'in']);
28728         //this.el.hide();
28729         
28730     }
28731     
28732 });
28733  
28734
28735  /*
28736  * - LGPL
28737  *
28738  * Location Picker
28739  * 
28740  */
28741
28742 /**
28743  * @class Roo.bootstrap.LocationPicker
28744  * @extends Roo.bootstrap.Component
28745  * Bootstrap LocationPicker class
28746  * @cfg {Number} latitude Position when init default 0
28747  * @cfg {Number} longitude Position when init default 0
28748  * @cfg {Number} zoom default 15
28749  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28750  * @cfg {Boolean} mapTypeControl default false
28751  * @cfg {Boolean} disableDoubleClickZoom default false
28752  * @cfg {Boolean} scrollwheel default true
28753  * @cfg {Boolean} streetViewControl default false
28754  * @cfg {Number} radius default 0
28755  * @cfg {String} locationName
28756  * @cfg {Boolean} draggable default true
28757  * @cfg {Boolean} enableAutocomplete default false
28758  * @cfg {Boolean} enableReverseGeocode default true
28759  * @cfg {String} markerTitle
28760  * 
28761  * @constructor
28762  * Create a new LocationPicker
28763  * @param {Object} config The config object
28764  */
28765
28766
28767 Roo.bootstrap.LocationPicker = function(config){
28768     
28769     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28770     
28771     this.addEvents({
28772         /**
28773          * @event initial
28774          * Fires when the picker initialized.
28775          * @param {Roo.bootstrap.LocationPicker} this
28776          * @param {Google Location} location
28777          */
28778         initial : true,
28779         /**
28780          * @event positionchanged
28781          * Fires when the picker position changed.
28782          * @param {Roo.bootstrap.LocationPicker} this
28783          * @param {Google Location} location
28784          */
28785         positionchanged : true,
28786         /**
28787          * @event resize
28788          * Fires when the map resize.
28789          * @param {Roo.bootstrap.LocationPicker} this
28790          */
28791         resize : true,
28792         /**
28793          * @event show
28794          * Fires when the map show.
28795          * @param {Roo.bootstrap.LocationPicker} this
28796          */
28797         show : true,
28798         /**
28799          * @event hide
28800          * Fires when the map hide.
28801          * @param {Roo.bootstrap.LocationPicker} this
28802          */
28803         hide : true,
28804         /**
28805          * @event mapClick
28806          * Fires when click the map.
28807          * @param {Roo.bootstrap.LocationPicker} this
28808          * @param {Map event} e
28809          */
28810         mapClick : true,
28811         /**
28812          * @event mapRightClick
28813          * Fires when right click the map.
28814          * @param {Roo.bootstrap.LocationPicker} this
28815          * @param {Map event} e
28816          */
28817         mapRightClick : true,
28818         /**
28819          * @event markerClick
28820          * Fires when click the marker.
28821          * @param {Roo.bootstrap.LocationPicker} this
28822          * @param {Map event} e
28823          */
28824         markerClick : true,
28825         /**
28826          * @event markerRightClick
28827          * Fires when right click the marker.
28828          * @param {Roo.bootstrap.LocationPicker} this
28829          * @param {Map event} e
28830          */
28831         markerRightClick : true,
28832         /**
28833          * @event OverlayViewDraw
28834          * Fires when OverlayView Draw
28835          * @param {Roo.bootstrap.LocationPicker} this
28836          */
28837         OverlayViewDraw : true,
28838         /**
28839          * @event OverlayViewOnAdd
28840          * Fires when OverlayView Draw
28841          * @param {Roo.bootstrap.LocationPicker} this
28842          */
28843         OverlayViewOnAdd : true,
28844         /**
28845          * @event OverlayViewOnRemove
28846          * Fires when OverlayView Draw
28847          * @param {Roo.bootstrap.LocationPicker} this
28848          */
28849         OverlayViewOnRemove : true,
28850         /**
28851          * @event OverlayViewShow
28852          * Fires when OverlayView Draw
28853          * @param {Roo.bootstrap.LocationPicker} this
28854          * @param {Pixel} cpx
28855          */
28856         OverlayViewShow : true,
28857         /**
28858          * @event OverlayViewHide
28859          * Fires when OverlayView Draw
28860          * @param {Roo.bootstrap.LocationPicker} this
28861          */
28862         OverlayViewHide : true,
28863         /**
28864          * @event loadexception
28865          * Fires when load google lib failed.
28866          * @param {Roo.bootstrap.LocationPicker} this
28867          */
28868         loadexception : true
28869     });
28870         
28871 };
28872
28873 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28874     
28875     gMapContext: false,
28876     
28877     latitude: 0,
28878     longitude: 0,
28879     zoom: 15,
28880     mapTypeId: false,
28881     mapTypeControl: false,
28882     disableDoubleClickZoom: false,
28883     scrollwheel: true,
28884     streetViewControl: false,
28885     radius: 0,
28886     locationName: '',
28887     draggable: true,
28888     enableAutocomplete: false,
28889     enableReverseGeocode: true,
28890     markerTitle: '',
28891     
28892     getAutoCreate: function()
28893     {
28894
28895         var cfg = {
28896             tag: 'div',
28897             cls: 'roo-location-picker'
28898         };
28899         
28900         return cfg
28901     },
28902     
28903     initEvents: function(ct, position)
28904     {       
28905         if(!this.el.getWidth() || this.isApplied()){
28906             return;
28907         }
28908         
28909         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28910         
28911         this.initial();
28912     },
28913     
28914     initial: function()
28915     {
28916         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28917             this.fireEvent('loadexception', this);
28918             return;
28919         }
28920         
28921         if(!this.mapTypeId){
28922             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28923         }
28924         
28925         this.gMapContext = this.GMapContext();
28926         
28927         this.initOverlayView();
28928         
28929         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28930         
28931         var _this = this;
28932                 
28933         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28934             _this.setPosition(_this.gMapContext.marker.position);
28935         });
28936         
28937         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28938             _this.fireEvent('mapClick', this, event);
28939             
28940         });
28941
28942         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28943             _this.fireEvent('mapRightClick', this, event);
28944             
28945         });
28946         
28947         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28948             _this.fireEvent('markerClick', this, event);
28949             
28950         });
28951
28952         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28953             _this.fireEvent('markerRightClick', this, event);
28954             
28955         });
28956         
28957         this.setPosition(this.gMapContext.location);
28958         
28959         this.fireEvent('initial', this, this.gMapContext.location);
28960     },
28961     
28962     initOverlayView: function()
28963     {
28964         var _this = this;
28965         
28966         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28967             
28968             draw: function()
28969             {
28970                 _this.fireEvent('OverlayViewDraw', _this);
28971             },
28972             
28973             onAdd: function()
28974             {
28975                 _this.fireEvent('OverlayViewOnAdd', _this);
28976             },
28977             
28978             onRemove: function()
28979             {
28980                 _this.fireEvent('OverlayViewOnRemove', _this);
28981             },
28982             
28983             show: function(cpx)
28984             {
28985                 _this.fireEvent('OverlayViewShow', _this, cpx);
28986             },
28987             
28988             hide: function()
28989             {
28990                 _this.fireEvent('OverlayViewHide', _this);
28991             }
28992             
28993         });
28994     },
28995     
28996     fromLatLngToContainerPixel: function(event)
28997     {
28998         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28999     },
29000     
29001     isApplied: function() 
29002     {
29003         return this.getGmapContext() == false ? false : true;
29004     },
29005     
29006     getGmapContext: function() 
29007     {
29008         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29009     },
29010     
29011     GMapContext: function() 
29012     {
29013         var position = new google.maps.LatLng(this.latitude, this.longitude);
29014         
29015         var _map = new google.maps.Map(this.el.dom, {
29016             center: position,
29017             zoom: this.zoom,
29018             mapTypeId: this.mapTypeId,
29019             mapTypeControl: this.mapTypeControl,
29020             disableDoubleClickZoom: this.disableDoubleClickZoom,
29021             scrollwheel: this.scrollwheel,
29022             streetViewControl: this.streetViewControl,
29023             locationName: this.locationName,
29024             draggable: this.draggable,
29025             enableAutocomplete: this.enableAutocomplete,
29026             enableReverseGeocode: this.enableReverseGeocode
29027         });
29028         
29029         var _marker = new google.maps.Marker({
29030             position: position,
29031             map: _map,
29032             title: this.markerTitle,
29033             draggable: this.draggable
29034         });
29035         
29036         return {
29037             map: _map,
29038             marker: _marker,
29039             circle: null,
29040             location: position,
29041             radius: this.radius,
29042             locationName: this.locationName,
29043             addressComponents: {
29044                 formatted_address: null,
29045                 addressLine1: null,
29046                 addressLine2: null,
29047                 streetName: null,
29048                 streetNumber: null,
29049                 city: null,
29050                 district: null,
29051                 state: null,
29052                 stateOrProvince: null
29053             },
29054             settings: this,
29055             domContainer: this.el.dom,
29056             geodecoder: new google.maps.Geocoder()
29057         };
29058     },
29059     
29060     drawCircle: function(center, radius, options) 
29061     {
29062         if (this.gMapContext.circle != null) {
29063             this.gMapContext.circle.setMap(null);
29064         }
29065         if (radius > 0) {
29066             radius *= 1;
29067             options = Roo.apply({}, options, {
29068                 strokeColor: "#0000FF",
29069                 strokeOpacity: .35,
29070                 strokeWeight: 2,
29071                 fillColor: "#0000FF",
29072                 fillOpacity: .2
29073             });
29074             
29075             options.map = this.gMapContext.map;
29076             options.radius = radius;
29077             options.center = center;
29078             this.gMapContext.circle = new google.maps.Circle(options);
29079             return this.gMapContext.circle;
29080         }
29081         
29082         return null;
29083     },
29084     
29085     setPosition: function(location) 
29086     {
29087         this.gMapContext.location = location;
29088         this.gMapContext.marker.setPosition(location);
29089         this.gMapContext.map.panTo(location);
29090         this.drawCircle(location, this.gMapContext.radius, {});
29091         
29092         var _this = this;
29093         
29094         if (this.gMapContext.settings.enableReverseGeocode) {
29095             this.gMapContext.geodecoder.geocode({
29096                 latLng: this.gMapContext.location
29097             }, function(results, status) {
29098                 
29099                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29100                     _this.gMapContext.locationName = results[0].formatted_address;
29101                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29102                     
29103                     _this.fireEvent('positionchanged', this, location);
29104                 }
29105             });
29106             
29107             return;
29108         }
29109         
29110         this.fireEvent('positionchanged', this, location);
29111     },
29112     
29113     resize: function()
29114     {
29115         google.maps.event.trigger(this.gMapContext.map, "resize");
29116         
29117         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29118         
29119         this.fireEvent('resize', this);
29120     },
29121     
29122     setPositionByLatLng: function(latitude, longitude)
29123     {
29124         this.setPosition(new google.maps.LatLng(latitude, longitude));
29125     },
29126     
29127     getCurrentPosition: function() 
29128     {
29129         return {
29130             latitude: this.gMapContext.location.lat(),
29131             longitude: this.gMapContext.location.lng()
29132         };
29133     },
29134     
29135     getAddressName: function() 
29136     {
29137         return this.gMapContext.locationName;
29138     },
29139     
29140     getAddressComponents: function() 
29141     {
29142         return this.gMapContext.addressComponents;
29143     },
29144     
29145     address_component_from_google_geocode: function(address_components) 
29146     {
29147         var result = {};
29148         
29149         for (var i = 0; i < address_components.length; i++) {
29150             var component = address_components[i];
29151             if (component.types.indexOf("postal_code") >= 0) {
29152                 result.postalCode = component.short_name;
29153             } else if (component.types.indexOf("street_number") >= 0) {
29154                 result.streetNumber = component.short_name;
29155             } else if (component.types.indexOf("route") >= 0) {
29156                 result.streetName = component.short_name;
29157             } else if (component.types.indexOf("neighborhood") >= 0) {
29158                 result.city = component.short_name;
29159             } else if (component.types.indexOf("locality") >= 0) {
29160                 result.city = component.short_name;
29161             } else if (component.types.indexOf("sublocality") >= 0) {
29162                 result.district = component.short_name;
29163             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29164                 result.stateOrProvince = component.short_name;
29165             } else if (component.types.indexOf("country") >= 0) {
29166                 result.country = component.short_name;
29167             }
29168         }
29169         
29170         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29171         result.addressLine2 = "";
29172         return result;
29173     },
29174     
29175     setZoomLevel: function(zoom)
29176     {
29177         this.gMapContext.map.setZoom(zoom);
29178     },
29179     
29180     show: function()
29181     {
29182         if(!this.el){
29183             return;
29184         }
29185         
29186         this.el.show();
29187         
29188         this.resize();
29189         
29190         this.fireEvent('show', this);
29191     },
29192     
29193     hide: function()
29194     {
29195         if(!this.el){
29196             return;
29197         }
29198         
29199         this.el.hide();
29200         
29201         this.fireEvent('hide', this);
29202     }
29203     
29204 });
29205
29206 Roo.apply(Roo.bootstrap.LocationPicker, {
29207     
29208     OverlayView : function(map, options)
29209     {
29210         options = options || {};
29211         
29212         this.setMap(map);
29213     }
29214     
29215     
29216 });/**
29217  * @class Roo.bootstrap.Alert
29218  * @extends Roo.bootstrap.Component
29219  * Bootstrap Alert class - shows an alert area box
29220  * eg
29221  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29222   Enter a valid email address
29223 </div>
29224  * @licence LGPL
29225  * @cfg {String} title The title of alert
29226  * @cfg {String} html The content of alert
29227  * @cfg {String} weight (  success | info | warning | danger )
29228  * @cfg {String} faicon font-awesomeicon
29229  * 
29230  * @constructor
29231  * Create a new alert
29232  * @param {Object} config The config object
29233  */
29234
29235
29236 Roo.bootstrap.Alert = function(config){
29237     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29238     
29239 };
29240
29241 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29242     
29243     title: '',
29244     html: '',
29245     weight: false,
29246     faicon: false,
29247     
29248     getAutoCreate : function()
29249     {
29250         
29251         var cfg = {
29252             tag : 'div',
29253             cls : 'alert',
29254             cn : [
29255                 {
29256                     tag : 'i',
29257                     cls : 'roo-alert-icon'
29258                     
29259                 },
29260                 {
29261                     tag : 'b',
29262                     cls : 'roo-alert-title',
29263                     html : this.title
29264                 },
29265                 {
29266                     tag : 'span',
29267                     cls : 'roo-alert-text',
29268                     html : this.html
29269                 }
29270             ]
29271         };
29272         
29273         if(this.faicon){
29274             cfg.cn[0].cls += ' fa ' + this.faicon;
29275         }
29276         
29277         if(this.weight){
29278             cfg.cls += ' alert-' + this.weight;
29279         }
29280         
29281         return cfg;
29282     },
29283     
29284     initEvents: function() 
29285     {
29286         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29287     },
29288     
29289     setTitle : function(str)
29290     {
29291         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29292     },
29293     
29294     setText : function(str)
29295     {
29296         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29297     },
29298     
29299     setWeight : function(weight)
29300     {
29301         if(this.weight){
29302             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29303         }
29304         
29305         this.weight = weight;
29306         
29307         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29308     },
29309     
29310     setIcon : function(icon)
29311     {
29312         if(this.faicon){
29313             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29314         }
29315         
29316         this.faicon = icon;
29317         
29318         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29319     },
29320     
29321     hide: function() 
29322     {
29323         this.el.hide();   
29324     },
29325     
29326     show: function() 
29327     {  
29328         this.el.show();   
29329     }
29330     
29331 });
29332
29333  
29334 /*
29335 * Licence: LGPL
29336 */
29337
29338 /**
29339  * @class Roo.bootstrap.UploadCropbox
29340  * @extends Roo.bootstrap.Component
29341  * Bootstrap UploadCropbox class
29342  * @cfg {String} emptyText show when image has been loaded
29343  * @cfg {String} rotateNotify show when image too small to rotate
29344  * @cfg {Number} errorTimeout default 3000
29345  * @cfg {Number} minWidth default 300
29346  * @cfg {Number} minHeight default 300
29347  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29348  * @cfg {Boolean} isDocument (true|false) default false
29349  * @cfg {String} url action url
29350  * @cfg {String} paramName default 'imageUpload'
29351  * @cfg {String} method default POST
29352  * @cfg {Boolean} loadMask (true|false) default true
29353  * @cfg {Boolean} loadingText default 'Loading...'
29354  * 
29355  * @constructor
29356  * Create a new UploadCropbox
29357  * @param {Object} config The config object
29358  */
29359
29360 Roo.bootstrap.UploadCropbox = function(config){
29361     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29362     
29363     this.addEvents({
29364         /**
29365          * @event beforeselectfile
29366          * Fire before select file
29367          * @param {Roo.bootstrap.UploadCropbox} this
29368          */
29369         "beforeselectfile" : true,
29370         /**
29371          * @event initial
29372          * Fire after initEvent
29373          * @param {Roo.bootstrap.UploadCropbox} this
29374          */
29375         "initial" : true,
29376         /**
29377          * @event crop
29378          * Fire after initEvent
29379          * @param {Roo.bootstrap.UploadCropbox} this
29380          * @param {String} data
29381          */
29382         "crop" : true,
29383         /**
29384          * @event prepare
29385          * Fire when preparing the file data
29386          * @param {Roo.bootstrap.UploadCropbox} this
29387          * @param {Object} file
29388          */
29389         "prepare" : true,
29390         /**
29391          * @event exception
29392          * Fire when get exception
29393          * @param {Roo.bootstrap.UploadCropbox} this
29394          * @param {XMLHttpRequest} xhr
29395          */
29396         "exception" : true,
29397         /**
29398          * @event beforeloadcanvas
29399          * Fire before load the canvas
29400          * @param {Roo.bootstrap.UploadCropbox} this
29401          * @param {String} src
29402          */
29403         "beforeloadcanvas" : true,
29404         /**
29405          * @event trash
29406          * Fire when trash image
29407          * @param {Roo.bootstrap.UploadCropbox} this
29408          */
29409         "trash" : true,
29410         /**
29411          * @event download
29412          * Fire when download the image
29413          * @param {Roo.bootstrap.UploadCropbox} this
29414          */
29415         "download" : true,
29416         /**
29417          * @event footerbuttonclick
29418          * Fire when footerbuttonclick
29419          * @param {Roo.bootstrap.UploadCropbox} this
29420          * @param {String} type
29421          */
29422         "footerbuttonclick" : true,
29423         /**
29424          * @event resize
29425          * Fire when resize
29426          * @param {Roo.bootstrap.UploadCropbox} this
29427          */
29428         "resize" : true,
29429         /**
29430          * @event rotate
29431          * Fire when rotate the image
29432          * @param {Roo.bootstrap.UploadCropbox} this
29433          * @param {String} pos
29434          */
29435         "rotate" : true,
29436         /**
29437          * @event inspect
29438          * Fire when inspect the file
29439          * @param {Roo.bootstrap.UploadCropbox} this
29440          * @param {Object} file
29441          */
29442         "inspect" : true,
29443         /**
29444          * @event upload
29445          * Fire when xhr upload the file
29446          * @param {Roo.bootstrap.UploadCropbox} this
29447          * @param {Object} data
29448          */
29449         "upload" : true,
29450         /**
29451          * @event arrange
29452          * Fire when arrange the file data
29453          * @param {Roo.bootstrap.UploadCropbox} this
29454          * @param {Object} formData
29455          */
29456         "arrange" : true
29457     });
29458     
29459     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29460 };
29461
29462 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29463     
29464     emptyText : 'Click to upload image',
29465     rotateNotify : 'Image is too small to rotate',
29466     errorTimeout : 3000,
29467     scale : 0,
29468     baseScale : 1,
29469     rotate : 0,
29470     dragable : false,
29471     pinching : false,
29472     mouseX : 0,
29473     mouseY : 0,
29474     cropData : false,
29475     minWidth : 300,
29476     minHeight : 300,
29477     file : false,
29478     exif : {},
29479     baseRotate : 1,
29480     cropType : 'image/jpeg',
29481     buttons : false,
29482     canvasLoaded : false,
29483     isDocument : false,
29484     method : 'POST',
29485     paramName : 'imageUpload',
29486     loadMask : true,
29487     loadingText : 'Loading...',
29488     maskEl : false,
29489     
29490     getAutoCreate : function()
29491     {
29492         var cfg = {
29493             tag : 'div',
29494             cls : 'roo-upload-cropbox',
29495             cn : [
29496                 {
29497                     tag : 'input',
29498                     cls : 'roo-upload-cropbox-selector',
29499                     type : 'file'
29500                 },
29501                 {
29502                     tag : 'div',
29503                     cls : 'roo-upload-cropbox-body',
29504                     style : 'cursor:pointer',
29505                     cn : [
29506                         {
29507                             tag : 'div',
29508                             cls : 'roo-upload-cropbox-preview'
29509                         },
29510                         {
29511                             tag : 'div',
29512                             cls : 'roo-upload-cropbox-thumb'
29513                         },
29514                         {
29515                             tag : 'div',
29516                             cls : 'roo-upload-cropbox-empty-notify',
29517                             html : this.emptyText
29518                         },
29519                         {
29520                             tag : 'div',
29521                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29522                             html : this.rotateNotify
29523                         }
29524                     ]
29525                 },
29526                 {
29527                     tag : 'div',
29528                     cls : 'roo-upload-cropbox-footer',
29529                     cn : {
29530                         tag : 'div',
29531                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29532                         cn : []
29533                     }
29534                 }
29535             ]
29536         };
29537         
29538         return cfg;
29539     },
29540     
29541     onRender : function(ct, position)
29542     {
29543         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29544         
29545         if (this.buttons.length) {
29546             
29547             Roo.each(this.buttons, function(bb) {
29548                 
29549                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29550                 
29551                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29552                 
29553             }, this);
29554         }
29555         
29556         if(this.loadMask){
29557             this.maskEl = this.el;
29558         }
29559     },
29560     
29561     initEvents : function()
29562     {
29563         this.urlAPI = (window.createObjectURL && window) || 
29564                                 (window.URL && URL.revokeObjectURL && URL) || 
29565                                 (window.webkitURL && webkitURL);
29566                         
29567         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29568         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29569         
29570         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29571         this.selectorEl.hide();
29572         
29573         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29574         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29575         
29576         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29577         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29578         this.thumbEl.hide();
29579         
29580         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29581         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29582         
29583         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29584         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29585         this.errorEl.hide();
29586         
29587         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29588         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29589         this.footerEl.hide();
29590         
29591         this.setThumbBoxSize();
29592         
29593         this.bind();
29594         
29595         this.resize();
29596         
29597         this.fireEvent('initial', this);
29598     },
29599
29600     bind : function()
29601     {
29602         var _this = this;
29603         
29604         window.addEventListener("resize", function() { _this.resize(); } );
29605         
29606         this.bodyEl.on('click', this.beforeSelectFile, this);
29607         
29608         if(Roo.isTouch){
29609             this.bodyEl.on('touchstart', this.onTouchStart, this);
29610             this.bodyEl.on('touchmove', this.onTouchMove, this);
29611             this.bodyEl.on('touchend', this.onTouchEnd, this);
29612         }
29613         
29614         if(!Roo.isTouch){
29615             this.bodyEl.on('mousedown', this.onMouseDown, this);
29616             this.bodyEl.on('mousemove', this.onMouseMove, this);
29617             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29618             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29619             Roo.get(document).on('mouseup', this.onMouseUp, this);
29620         }
29621         
29622         this.selectorEl.on('change', this.onFileSelected, this);
29623     },
29624     
29625     reset : function()
29626     {    
29627         this.scale = 0;
29628         this.baseScale = 1;
29629         this.rotate = 0;
29630         this.baseRotate = 1;
29631         this.dragable = false;
29632         this.pinching = false;
29633         this.mouseX = 0;
29634         this.mouseY = 0;
29635         this.cropData = false;
29636         this.notifyEl.dom.innerHTML = this.emptyText;
29637         
29638         this.selectorEl.dom.value = '';
29639         
29640     },
29641     
29642     resize : function()
29643     {
29644         if(this.fireEvent('resize', this) != false){
29645             this.setThumbBoxPosition();
29646             this.setCanvasPosition();
29647         }
29648     },
29649     
29650     onFooterButtonClick : function(e, el, o, type)
29651     {
29652         switch (type) {
29653             case 'rotate-left' :
29654                 this.onRotateLeft(e);
29655                 break;
29656             case 'rotate-right' :
29657                 this.onRotateRight(e);
29658                 break;
29659             case 'picture' :
29660                 this.beforeSelectFile(e);
29661                 break;
29662             case 'trash' :
29663                 this.trash(e);
29664                 break;
29665             case 'crop' :
29666                 this.crop(e);
29667                 break;
29668             case 'download' :
29669                 this.download(e);
29670                 break;
29671             default :
29672                 break;
29673         }
29674         
29675         this.fireEvent('footerbuttonclick', this, type);
29676     },
29677     
29678     beforeSelectFile : function(e)
29679     {
29680         e.preventDefault();
29681         
29682         if(this.fireEvent('beforeselectfile', this) != false){
29683             this.selectorEl.dom.click();
29684         }
29685     },
29686     
29687     onFileSelected : function(e)
29688     {
29689         e.preventDefault();
29690         
29691         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29692             return;
29693         }
29694         
29695         var file = this.selectorEl.dom.files[0];
29696         
29697         if(this.fireEvent('inspect', this, file) != false){
29698             this.prepare(file);
29699         }
29700         
29701     },
29702     
29703     trash : function(e)
29704     {
29705         this.fireEvent('trash', this);
29706     },
29707     
29708     download : function(e)
29709     {
29710         this.fireEvent('download', this);
29711     },
29712     
29713     loadCanvas : function(src)
29714     {   
29715         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29716             
29717             this.reset();
29718             
29719             this.imageEl = document.createElement('img');
29720             
29721             var _this = this;
29722             
29723             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29724             
29725             this.imageEl.src = src;
29726         }
29727     },
29728     
29729     onLoadCanvas : function()
29730     {   
29731         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29732         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29733         
29734         this.bodyEl.un('click', this.beforeSelectFile, this);
29735         
29736         this.notifyEl.hide();
29737         this.thumbEl.show();
29738         this.footerEl.show();
29739         
29740         this.baseRotateLevel();
29741         
29742         if(this.isDocument){
29743             this.setThumbBoxSize();
29744         }
29745         
29746         this.setThumbBoxPosition();
29747         
29748         this.baseScaleLevel();
29749         
29750         this.draw();
29751         
29752         this.resize();
29753         
29754         this.canvasLoaded = true;
29755         
29756         if(this.loadMask){
29757             this.maskEl.unmask();
29758         }
29759         
29760     },
29761     
29762     setCanvasPosition : function()
29763     {   
29764         if(!this.canvasEl){
29765             return;
29766         }
29767         
29768         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29769         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29770         
29771         this.previewEl.setLeft(pw);
29772         this.previewEl.setTop(ph);
29773         
29774     },
29775     
29776     onMouseDown : function(e)
29777     {   
29778         e.stopEvent();
29779         
29780         this.dragable = true;
29781         this.pinching = false;
29782         
29783         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29784             this.dragable = false;
29785             return;
29786         }
29787         
29788         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29789         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29790         
29791     },
29792     
29793     onMouseMove : function(e)
29794     {   
29795         e.stopEvent();
29796         
29797         if(!this.canvasLoaded){
29798             return;
29799         }
29800         
29801         if (!this.dragable){
29802             return;
29803         }
29804         
29805         var minX = Math.ceil(this.thumbEl.getLeft(true));
29806         var minY = Math.ceil(this.thumbEl.getTop(true));
29807         
29808         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29809         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29810         
29811         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29812         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29813         
29814         x = x - this.mouseX;
29815         y = y - this.mouseY;
29816         
29817         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29818         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29819         
29820         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29821         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29822         
29823         this.previewEl.setLeft(bgX);
29824         this.previewEl.setTop(bgY);
29825         
29826         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29827         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29828     },
29829     
29830     onMouseUp : function(e)
29831     {   
29832         e.stopEvent();
29833         
29834         this.dragable = false;
29835     },
29836     
29837     onMouseWheel : function(e)
29838     {   
29839         e.stopEvent();
29840         
29841         this.startScale = this.scale;
29842         
29843         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29844         
29845         if(!this.zoomable()){
29846             this.scale = this.startScale;
29847             return;
29848         }
29849         
29850         this.draw();
29851         
29852         return;
29853     },
29854     
29855     zoomable : function()
29856     {
29857         var minScale = this.thumbEl.getWidth() / this.minWidth;
29858         
29859         if(this.minWidth < this.minHeight){
29860             minScale = this.thumbEl.getHeight() / this.minHeight;
29861         }
29862         
29863         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29864         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29865         
29866         if(
29867                 this.isDocument &&
29868                 (this.rotate == 0 || this.rotate == 180) && 
29869                 (
29870                     width > this.imageEl.OriginWidth || 
29871                     height > this.imageEl.OriginHeight ||
29872                     (width < this.minWidth && height < this.minHeight)
29873                 )
29874         ){
29875             return false;
29876         }
29877         
29878         if(
29879                 this.isDocument &&
29880                 (this.rotate == 90 || this.rotate == 270) && 
29881                 (
29882                     width > this.imageEl.OriginWidth || 
29883                     height > this.imageEl.OriginHeight ||
29884                     (width < this.minHeight && height < this.minWidth)
29885                 )
29886         ){
29887             return false;
29888         }
29889         
29890         if(
29891                 !this.isDocument &&
29892                 (this.rotate == 0 || this.rotate == 180) && 
29893                 (
29894                     width < this.minWidth || 
29895                     width > this.imageEl.OriginWidth || 
29896                     height < this.minHeight || 
29897                     height > this.imageEl.OriginHeight
29898                 )
29899         ){
29900             return false;
29901         }
29902         
29903         if(
29904                 !this.isDocument &&
29905                 (this.rotate == 90 || this.rotate == 270) && 
29906                 (
29907                     width < this.minHeight || 
29908                     width > this.imageEl.OriginWidth || 
29909                     height < this.minWidth || 
29910                     height > this.imageEl.OriginHeight
29911                 )
29912         ){
29913             return false;
29914         }
29915         
29916         return true;
29917         
29918     },
29919     
29920     onRotateLeft : function(e)
29921     {   
29922         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29923             
29924             var minScale = this.thumbEl.getWidth() / this.minWidth;
29925             
29926             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29927             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29928             
29929             this.startScale = this.scale;
29930             
29931             while (this.getScaleLevel() < minScale){
29932             
29933                 this.scale = this.scale + 1;
29934                 
29935                 if(!this.zoomable()){
29936                     break;
29937                 }
29938                 
29939                 if(
29940                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29941                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29942                 ){
29943                     continue;
29944                 }
29945                 
29946                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29947
29948                 this.draw();
29949                 
29950                 return;
29951             }
29952             
29953             this.scale = this.startScale;
29954             
29955             this.onRotateFail();
29956             
29957             return false;
29958         }
29959         
29960         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29961
29962         if(this.isDocument){
29963             this.setThumbBoxSize();
29964             this.setThumbBoxPosition();
29965             this.setCanvasPosition();
29966         }
29967         
29968         this.draw();
29969         
29970         this.fireEvent('rotate', this, 'left');
29971         
29972     },
29973     
29974     onRotateRight : function(e)
29975     {
29976         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29977             
29978             var minScale = this.thumbEl.getWidth() / this.minWidth;
29979         
29980             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29981             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29982             
29983             this.startScale = this.scale;
29984             
29985             while (this.getScaleLevel() < minScale){
29986             
29987                 this.scale = this.scale + 1;
29988                 
29989                 if(!this.zoomable()){
29990                     break;
29991                 }
29992                 
29993                 if(
29994                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29995                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29996                 ){
29997                     continue;
29998                 }
29999                 
30000                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30001
30002                 this.draw();
30003                 
30004                 return;
30005             }
30006             
30007             this.scale = this.startScale;
30008             
30009             this.onRotateFail();
30010             
30011             return false;
30012         }
30013         
30014         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30015
30016         if(this.isDocument){
30017             this.setThumbBoxSize();
30018             this.setThumbBoxPosition();
30019             this.setCanvasPosition();
30020         }
30021         
30022         this.draw();
30023         
30024         this.fireEvent('rotate', this, 'right');
30025     },
30026     
30027     onRotateFail : function()
30028     {
30029         this.errorEl.show(true);
30030         
30031         var _this = this;
30032         
30033         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30034     },
30035     
30036     draw : function()
30037     {
30038         this.previewEl.dom.innerHTML = '';
30039         
30040         var canvasEl = document.createElement("canvas");
30041         
30042         var contextEl = canvasEl.getContext("2d");
30043         
30044         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30045         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30046         var center = this.imageEl.OriginWidth / 2;
30047         
30048         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30049             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30050             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30051             center = this.imageEl.OriginHeight / 2;
30052         }
30053         
30054         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30055         
30056         contextEl.translate(center, center);
30057         contextEl.rotate(this.rotate * Math.PI / 180);
30058
30059         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30060         
30061         this.canvasEl = document.createElement("canvas");
30062         
30063         this.contextEl = this.canvasEl.getContext("2d");
30064         
30065         switch (this.rotate) {
30066             case 0 :
30067                 
30068                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30069                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30070                 
30071                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30072                 
30073                 break;
30074             case 90 : 
30075                 
30076                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30077                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30078                 
30079                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30080                     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);
30081                     break;
30082                 }
30083                 
30084                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30085                 
30086                 break;
30087             case 180 :
30088                 
30089                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30090                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30091                 
30092                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30093                     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);
30094                     break;
30095                 }
30096                 
30097                 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);
30098                 
30099                 break;
30100             case 270 :
30101                 
30102                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30103                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30104         
30105                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30106                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30107                     break;
30108                 }
30109                 
30110                 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);
30111                 
30112                 break;
30113             default : 
30114                 break;
30115         }
30116         
30117         this.previewEl.appendChild(this.canvasEl);
30118         
30119         this.setCanvasPosition();
30120     },
30121     
30122     crop : function()
30123     {
30124         if(!this.canvasLoaded){
30125             return;
30126         }
30127         
30128         var imageCanvas = document.createElement("canvas");
30129         
30130         var imageContext = imageCanvas.getContext("2d");
30131         
30132         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30133         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30134         
30135         var center = imageCanvas.width / 2;
30136         
30137         imageContext.translate(center, center);
30138         
30139         imageContext.rotate(this.rotate * Math.PI / 180);
30140         
30141         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30142         
30143         var canvas = document.createElement("canvas");
30144         
30145         var context = canvas.getContext("2d");
30146                 
30147         canvas.width = this.minWidth;
30148         canvas.height = this.minHeight;
30149
30150         switch (this.rotate) {
30151             case 0 :
30152                 
30153                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30154                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30155                 
30156                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30157                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30158                 
30159                 var targetWidth = this.minWidth - 2 * x;
30160                 var targetHeight = this.minHeight - 2 * y;
30161                 
30162                 var scale = 1;
30163                 
30164                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30165                     scale = targetWidth / width;
30166                 }
30167                 
30168                 if(x > 0 && y == 0){
30169                     scale = targetHeight / height;
30170                 }
30171                 
30172                 if(x > 0 && y > 0){
30173                     scale = targetWidth / width;
30174                     
30175                     if(width < height){
30176                         scale = targetHeight / height;
30177                     }
30178                 }
30179                 
30180                 context.scale(scale, scale);
30181                 
30182                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30183                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30184
30185                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30186                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30187
30188                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30189                 
30190                 break;
30191             case 90 : 
30192                 
30193                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30194                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30195                 
30196                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30197                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30198                 
30199                 var targetWidth = this.minWidth - 2 * x;
30200                 var targetHeight = this.minHeight - 2 * y;
30201                 
30202                 var scale = 1;
30203                 
30204                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30205                     scale = targetWidth / width;
30206                 }
30207                 
30208                 if(x > 0 && y == 0){
30209                     scale = targetHeight / height;
30210                 }
30211                 
30212                 if(x > 0 && y > 0){
30213                     scale = targetWidth / width;
30214                     
30215                     if(width < height){
30216                         scale = targetHeight / height;
30217                     }
30218                 }
30219                 
30220                 context.scale(scale, scale);
30221                 
30222                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30223                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30224
30225                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30226                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30227                 
30228                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30229                 
30230                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30231                 
30232                 break;
30233             case 180 :
30234                 
30235                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30236                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30237                 
30238                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30239                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30240                 
30241                 var targetWidth = this.minWidth - 2 * x;
30242                 var targetHeight = this.minHeight - 2 * y;
30243                 
30244                 var scale = 1;
30245                 
30246                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30247                     scale = targetWidth / width;
30248                 }
30249                 
30250                 if(x > 0 && y == 0){
30251                     scale = targetHeight / height;
30252                 }
30253                 
30254                 if(x > 0 && y > 0){
30255                     scale = targetWidth / width;
30256                     
30257                     if(width < height){
30258                         scale = targetHeight / height;
30259                     }
30260                 }
30261                 
30262                 context.scale(scale, scale);
30263                 
30264                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30265                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30266
30267                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30268                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30269
30270                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30271                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30272                 
30273                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30274                 
30275                 break;
30276             case 270 :
30277                 
30278                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30279                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30280                 
30281                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30282                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30283                 
30284                 var targetWidth = this.minWidth - 2 * x;
30285                 var targetHeight = this.minHeight - 2 * y;
30286                 
30287                 var scale = 1;
30288                 
30289                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30290                     scale = targetWidth / width;
30291                 }
30292                 
30293                 if(x > 0 && y == 0){
30294                     scale = targetHeight / height;
30295                 }
30296                 
30297                 if(x > 0 && y > 0){
30298                     scale = targetWidth / width;
30299                     
30300                     if(width < height){
30301                         scale = targetHeight / height;
30302                     }
30303                 }
30304                 
30305                 context.scale(scale, scale);
30306                 
30307                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30308                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30309
30310                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30311                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30312                 
30313                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30314                 
30315                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30316                 
30317                 break;
30318             default : 
30319                 break;
30320         }
30321         
30322         this.cropData = canvas.toDataURL(this.cropType);
30323         
30324         if(this.fireEvent('crop', this, this.cropData) !== false){
30325             this.process(this.file, this.cropData);
30326         }
30327         
30328         return;
30329         
30330     },
30331     
30332     setThumbBoxSize : function()
30333     {
30334         var width, height;
30335         
30336         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30337             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30338             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30339             
30340             this.minWidth = width;
30341             this.minHeight = height;
30342             
30343             if(this.rotate == 90 || this.rotate == 270){
30344                 this.minWidth = height;
30345                 this.minHeight = width;
30346             }
30347         }
30348         
30349         height = 300;
30350         width = Math.ceil(this.minWidth * height / this.minHeight);
30351         
30352         if(this.minWidth > this.minHeight){
30353             width = 300;
30354             height = Math.ceil(this.minHeight * width / this.minWidth);
30355         }
30356         
30357         this.thumbEl.setStyle({
30358             width : width + 'px',
30359             height : height + 'px'
30360         });
30361
30362         return;
30363             
30364     },
30365     
30366     setThumbBoxPosition : function()
30367     {
30368         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30369         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30370         
30371         this.thumbEl.setLeft(x);
30372         this.thumbEl.setTop(y);
30373         
30374     },
30375     
30376     baseRotateLevel : function()
30377     {
30378         this.baseRotate = 1;
30379         
30380         if(
30381                 typeof(this.exif) != 'undefined' &&
30382                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30383                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30384         ){
30385             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30386         }
30387         
30388         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30389         
30390     },
30391     
30392     baseScaleLevel : function()
30393     {
30394         var width, height;
30395         
30396         if(this.isDocument){
30397             
30398             if(this.baseRotate == 6 || this.baseRotate == 8){
30399             
30400                 height = this.thumbEl.getHeight();
30401                 this.baseScale = height / this.imageEl.OriginWidth;
30402
30403                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30404                     width = this.thumbEl.getWidth();
30405                     this.baseScale = width / this.imageEl.OriginHeight;
30406                 }
30407
30408                 return;
30409             }
30410
30411             height = this.thumbEl.getHeight();
30412             this.baseScale = height / this.imageEl.OriginHeight;
30413
30414             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30415                 width = this.thumbEl.getWidth();
30416                 this.baseScale = width / this.imageEl.OriginWidth;
30417             }
30418
30419             return;
30420         }
30421         
30422         if(this.baseRotate == 6 || this.baseRotate == 8){
30423             
30424             width = this.thumbEl.getHeight();
30425             this.baseScale = width / this.imageEl.OriginHeight;
30426             
30427             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30428                 height = this.thumbEl.getWidth();
30429                 this.baseScale = height / this.imageEl.OriginHeight;
30430             }
30431             
30432             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30433                 height = this.thumbEl.getWidth();
30434                 this.baseScale = height / this.imageEl.OriginHeight;
30435                 
30436                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30437                     width = this.thumbEl.getHeight();
30438                     this.baseScale = width / this.imageEl.OriginWidth;
30439                 }
30440             }
30441             
30442             return;
30443         }
30444         
30445         width = this.thumbEl.getWidth();
30446         this.baseScale = width / this.imageEl.OriginWidth;
30447         
30448         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30449             height = this.thumbEl.getHeight();
30450             this.baseScale = height / this.imageEl.OriginHeight;
30451         }
30452         
30453         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30454             
30455             height = this.thumbEl.getHeight();
30456             this.baseScale = height / this.imageEl.OriginHeight;
30457             
30458             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30459                 width = this.thumbEl.getWidth();
30460                 this.baseScale = width / this.imageEl.OriginWidth;
30461             }
30462             
30463         }
30464         
30465         return;
30466     },
30467     
30468     getScaleLevel : function()
30469     {
30470         return this.baseScale * Math.pow(1.1, this.scale);
30471     },
30472     
30473     onTouchStart : function(e)
30474     {
30475         if(!this.canvasLoaded){
30476             this.beforeSelectFile(e);
30477             return;
30478         }
30479         
30480         var touches = e.browserEvent.touches;
30481         
30482         if(!touches){
30483             return;
30484         }
30485         
30486         if(touches.length == 1){
30487             this.onMouseDown(e);
30488             return;
30489         }
30490         
30491         if(touches.length != 2){
30492             return;
30493         }
30494         
30495         var coords = [];
30496         
30497         for(var i = 0, finger; finger = touches[i]; i++){
30498             coords.push(finger.pageX, finger.pageY);
30499         }
30500         
30501         var x = Math.pow(coords[0] - coords[2], 2);
30502         var y = Math.pow(coords[1] - coords[3], 2);
30503         
30504         this.startDistance = Math.sqrt(x + y);
30505         
30506         this.startScale = this.scale;
30507         
30508         this.pinching = true;
30509         this.dragable = false;
30510         
30511     },
30512     
30513     onTouchMove : function(e)
30514     {
30515         if(!this.pinching && !this.dragable){
30516             return;
30517         }
30518         
30519         var touches = e.browserEvent.touches;
30520         
30521         if(!touches){
30522             return;
30523         }
30524         
30525         if(this.dragable){
30526             this.onMouseMove(e);
30527             return;
30528         }
30529         
30530         var coords = [];
30531         
30532         for(var i = 0, finger; finger = touches[i]; i++){
30533             coords.push(finger.pageX, finger.pageY);
30534         }
30535         
30536         var x = Math.pow(coords[0] - coords[2], 2);
30537         var y = Math.pow(coords[1] - coords[3], 2);
30538         
30539         this.endDistance = Math.sqrt(x + y);
30540         
30541         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30542         
30543         if(!this.zoomable()){
30544             this.scale = this.startScale;
30545             return;
30546         }
30547         
30548         this.draw();
30549         
30550     },
30551     
30552     onTouchEnd : function(e)
30553     {
30554         this.pinching = false;
30555         this.dragable = false;
30556         
30557     },
30558     
30559     process : function(file, crop)
30560     {
30561         if(this.loadMask){
30562             this.maskEl.mask(this.loadingText);
30563         }
30564         
30565         this.xhr = new XMLHttpRequest();
30566         
30567         file.xhr = this.xhr;
30568
30569         this.xhr.open(this.method, this.url, true);
30570         
30571         var headers = {
30572             "Accept": "application/json",
30573             "Cache-Control": "no-cache",
30574             "X-Requested-With": "XMLHttpRequest"
30575         };
30576         
30577         for (var headerName in headers) {
30578             var headerValue = headers[headerName];
30579             if (headerValue) {
30580                 this.xhr.setRequestHeader(headerName, headerValue);
30581             }
30582         }
30583         
30584         var _this = this;
30585         
30586         this.xhr.onload = function()
30587         {
30588             _this.xhrOnLoad(_this.xhr);
30589         }
30590         
30591         this.xhr.onerror = function()
30592         {
30593             _this.xhrOnError(_this.xhr);
30594         }
30595         
30596         var formData = new FormData();
30597
30598         formData.append('returnHTML', 'NO');
30599         
30600         if(crop){
30601             formData.append('crop', crop);
30602         }
30603         
30604         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30605             formData.append(this.paramName, file, file.name);
30606         }
30607         
30608         if(typeof(file.filename) != 'undefined'){
30609             formData.append('filename', file.filename);
30610         }
30611         
30612         if(typeof(file.mimetype) != 'undefined'){
30613             formData.append('mimetype', file.mimetype);
30614         }
30615         
30616         if(this.fireEvent('arrange', this, formData) != false){
30617             this.xhr.send(formData);
30618         };
30619     },
30620     
30621     xhrOnLoad : function(xhr)
30622     {
30623         if(this.loadMask){
30624             this.maskEl.unmask();
30625         }
30626         
30627         if (xhr.readyState !== 4) {
30628             this.fireEvent('exception', this, xhr);
30629             return;
30630         }
30631
30632         var response = Roo.decode(xhr.responseText);
30633         
30634         if(!response.success){
30635             this.fireEvent('exception', this, xhr);
30636             return;
30637         }
30638         
30639         var response = Roo.decode(xhr.responseText);
30640         
30641         this.fireEvent('upload', this, response);
30642         
30643     },
30644     
30645     xhrOnError : function()
30646     {
30647         if(this.loadMask){
30648             this.maskEl.unmask();
30649         }
30650         
30651         Roo.log('xhr on error');
30652         
30653         var response = Roo.decode(xhr.responseText);
30654           
30655         Roo.log(response);
30656         
30657     },
30658     
30659     prepare : function(file)
30660     {   
30661         if(this.loadMask){
30662             this.maskEl.mask(this.loadingText);
30663         }
30664         
30665         this.file = false;
30666         this.exif = {};
30667         
30668         if(typeof(file) === 'string'){
30669             this.loadCanvas(file);
30670             return;
30671         }
30672         
30673         if(!file || !this.urlAPI){
30674             return;
30675         }
30676         
30677         this.file = file;
30678         this.cropType = file.type;
30679         
30680         var _this = this;
30681         
30682         if(this.fireEvent('prepare', this, this.file) != false){
30683             
30684             var reader = new FileReader();
30685             
30686             reader.onload = function (e) {
30687                 if (e.target.error) {
30688                     Roo.log(e.target.error);
30689                     return;
30690                 }
30691                 
30692                 var buffer = e.target.result,
30693                     dataView = new DataView(buffer),
30694                     offset = 2,
30695                     maxOffset = dataView.byteLength - 4,
30696                     markerBytes,
30697                     markerLength;
30698                 
30699                 if (dataView.getUint16(0) === 0xffd8) {
30700                     while (offset < maxOffset) {
30701                         markerBytes = dataView.getUint16(offset);
30702                         
30703                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30704                             markerLength = dataView.getUint16(offset + 2) + 2;
30705                             if (offset + markerLength > dataView.byteLength) {
30706                                 Roo.log('Invalid meta data: Invalid segment size.');
30707                                 break;
30708                             }
30709                             
30710                             if(markerBytes == 0xffe1){
30711                                 _this.parseExifData(
30712                                     dataView,
30713                                     offset,
30714                                     markerLength
30715                                 );
30716                             }
30717                             
30718                             offset += markerLength;
30719                             
30720                             continue;
30721                         }
30722                         
30723                         break;
30724                     }
30725                     
30726                 }
30727                 
30728                 var url = _this.urlAPI.createObjectURL(_this.file);
30729                 
30730                 _this.loadCanvas(url);
30731                 
30732                 return;
30733             }
30734             
30735             reader.readAsArrayBuffer(this.file);
30736             
30737         }
30738         
30739     },
30740     
30741     parseExifData : function(dataView, offset, length)
30742     {
30743         var tiffOffset = offset + 10,
30744             littleEndian,
30745             dirOffset;
30746     
30747         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30748             // No Exif data, might be XMP data instead
30749             return;
30750         }
30751         
30752         // Check for the ASCII code for "Exif" (0x45786966):
30753         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30754             // No Exif data, might be XMP data instead
30755             return;
30756         }
30757         if (tiffOffset + 8 > dataView.byteLength) {
30758             Roo.log('Invalid Exif data: Invalid segment size.');
30759             return;
30760         }
30761         // Check for the two null bytes:
30762         if (dataView.getUint16(offset + 8) !== 0x0000) {
30763             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30764             return;
30765         }
30766         // Check the byte alignment:
30767         switch (dataView.getUint16(tiffOffset)) {
30768         case 0x4949:
30769             littleEndian = true;
30770             break;
30771         case 0x4D4D:
30772             littleEndian = false;
30773             break;
30774         default:
30775             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30776             return;
30777         }
30778         // Check for the TIFF tag marker (0x002A):
30779         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30780             Roo.log('Invalid Exif data: Missing TIFF marker.');
30781             return;
30782         }
30783         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30784         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30785         
30786         this.parseExifTags(
30787             dataView,
30788             tiffOffset,
30789             tiffOffset + dirOffset,
30790             littleEndian
30791         );
30792     },
30793     
30794     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30795     {
30796         var tagsNumber,
30797             dirEndOffset,
30798             i;
30799         if (dirOffset + 6 > dataView.byteLength) {
30800             Roo.log('Invalid Exif data: Invalid directory offset.');
30801             return;
30802         }
30803         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30804         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30805         if (dirEndOffset + 4 > dataView.byteLength) {
30806             Roo.log('Invalid Exif data: Invalid directory size.');
30807             return;
30808         }
30809         for (i = 0; i < tagsNumber; i += 1) {
30810             this.parseExifTag(
30811                 dataView,
30812                 tiffOffset,
30813                 dirOffset + 2 + 12 * i, // tag offset
30814                 littleEndian
30815             );
30816         }
30817         // Return the offset to the next directory:
30818         return dataView.getUint32(dirEndOffset, littleEndian);
30819     },
30820     
30821     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30822     {
30823         var tag = dataView.getUint16(offset, littleEndian);
30824         
30825         this.exif[tag] = this.getExifValue(
30826             dataView,
30827             tiffOffset,
30828             offset,
30829             dataView.getUint16(offset + 2, littleEndian), // tag type
30830             dataView.getUint32(offset + 4, littleEndian), // tag length
30831             littleEndian
30832         );
30833     },
30834     
30835     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30836     {
30837         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30838             tagSize,
30839             dataOffset,
30840             values,
30841             i,
30842             str,
30843             c;
30844     
30845         if (!tagType) {
30846             Roo.log('Invalid Exif data: Invalid tag type.');
30847             return;
30848         }
30849         
30850         tagSize = tagType.size * length;
30851         // Determine if the value is contained in the dataOffset bytes,
30852         // or if the value at the dataOffset is a pointer to the actual data:
30853         dataOffset = tagSize > 4 ?
30854                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30855         if (dataOffset + tagSize > dataView.byteLength) {
30856             Roo.log('Invalid Exif data: Invalid data offset.');
30857             return;
30858         }
30859         if (length === 1) {
30860             return tagType.getValue(dataView, dataOffset, littleEndian);
30861         }
30862         values = [];
30863         for (i = 0; i < length; i += 1) {
30864             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30865         }
30866         
30867         if (tagType.ascii) {
30868             str = '';
30869             // Concatenate the chars:
30870             for (i = 0; i < values.length; i += 1) {
30871                 c = values[i];
30872                 // Ignore the terminating NULL byte(s):
30873                 if (c === '\u0000') {
30874                     break;
30875                 }
30876                 str += c;
30877             }
30878             return str;
30879         }
30880         return values;
30881     }
30882     
30883 });
30884
30885 Roo.apply(Roo.bootstrap.UploadCropbox, {
30886     tags : {
30887         'Orientation': 0x0112
30888     },
30889     
30890     Orientation: {
30891             1: 0, //'top-left',
30892 //            2: 'top-right',
30893             3: 180, //'bottom-right',
30894 //            4: 'bottom-left',
30895 //            5: 'left-top',
30896             6: 90, //'right-top',
30897 //            7: 'right-bottom',
30898             8: 270 //'left-bottom'
30899     },
30900     
30901     exifTagTypes : {
30902         // byte, 8-bit unsigned int:
30903         1: {
30904             getValue: function (dataView, dataOffset) {
30905                 return dataView.getUint8(dataOffset);
30906             },
30907             size: 1
30908         },
30909         // ascii, 8-bit byte:
30910         2: {
30911             getValue: function (dataView, dataOffset) {
30912                 return String.fromCharCode(dataView.getUint8(dataOffset));
30913             },
30914             size: 1,
30915             ascii: true
30916         },
30917         // short, 16 bit int:
30918         3: {
30919             getValue: function (dataView, dataOffset, littleEndian) {
30920                 return dataView.getUint16(dataOffset, littleEndian);
30921             },
30922             size: 2
30923         },
30924         // long, 32 bit int:
30925         4: {
30926             getValue: function (dataView, dataOffset, littleEndian) {
30927                 return dataView.getUint32(dataOffset, littleEndian);
30928             },
30929             size: 4
30930         },
30931         // rational = two long values, first is numerator, second is denominator:
30932         5: {
30933             getValue: function (dataView, dataOffset, littleEndian) {
30934                 return dataView.getUint32(dataOffset, littleEndian) /
30935                     dataView.getUint32(dataOffset + 4, littleEndian);
30936             },
30937             size: 8
30938         },
30939         // slong, 32 bit signed int:
30940         9: {
30941             getValue: function (dataView, dataOffset, littleEndian) {
30942                 return dataView.getInt32(dataOffset, littleEndian);
30943             },
30944             size: 4
30945         },
30946         // srational, two slongs, first is numerator, second is denominator:
30947         10: {
30948             getValue: function (dataView, dataOffset, littleEndian) {
30949                 return dataView.getInt32(dataOffset, littleEndian) /
30950                     dataView.getInt32(dataOffset + 4, littleEndian);
30951             },
30952             size: 8
30953         }
30954     },
30955     
30956     footer : {
30957         STANDARD : [
30958             {
30959                 tag : 'div',
30960                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30961                 action : 'rotate-left',
30962                 cn : [
30963                     {
30964                         tag : 'button',
30965                         cls : 'btn btn-default',
30966                         html : '<i class="fa fa-undo"></i>'
30967                     }
30968                 ]
30969             },
30970             {
30971                 tag : 'div',
30972                 cls : 'btn-group roo-upload-cropbox-picture',
30973                 action : 'picture',
30974                 cn : [
30975                     {
30976                         tag : 'button',
30977                         cls : 'btn btn-default',
30978                         html : '<i class="fa fa-picture-o"></i>'
30979                     }
30980                 ]
30981             },
30982             {
30983                 tag : 'div',
30984                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30985                 action : 'rotate-right',
30986                 cn : [
30987                     {
30988                         tag : 'button',
30989                         cls : 'btn btn-default',
30990                         html : '<i class="fa fa-repeat"></i>'
30991                     }
30992                 ]
30993             }
30994         ],
30995         DOCUMENT : [
30996             {
30997                 tag : 'div',
30998                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30999                 action : 'rotate-left',
31000                 cn : [
31001                     {
31002                         tag : 'button',
31003                         cls : 'btn btn-default',
31004                         html : '<i class="fa fa-undo"></i>'
31005                     }
31006                 ]
31007             },
31008             {
31009                 tag : 'div',
31010                 cls : 'btn-group roo-upload-cropbox-download',
31011                 action : 'download',
31012                 cn : [
31013                     {
31014                         tag : 'button',
31015                         cls : 'btn btn-default',
31016                         html : '<i class="fa fa-download"></i>'
31017                     }
31018                 ]
31019             },
31020             {
31021                 tag : 'div',
31022                 cls : 'btn-group roo-upload-cropbox-crop',
31023                 action : 'crop',
31024                 cn : [
31025                     {
31026                         tag : 'button',
31027                         cls : 'btn btn-default',
31028                         html : '<i class="fa fa-crop"></i>'
31029                     }
31030                 ]
31031             },
31032             {
31033                 tag : 'div',
31034                 cls : 'btn-group roo-upload-cropbox-trash',
31035                 action : 'trash',
31036                 cn : [
31037                     {
31038                         tag : 'button',
31039                         cls : 'btn btn-default',
31040                         html : '<i class="fa fa-trash"></i>'
31041                     }
31042                 ]
31043             },
31044             {
31045                 tag : 'div',
31046                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31047                 action : 'rotate-right',
31048                 cn : [
31049                     {
31050                         tag : 'button',
31051                         cls : 'btn btn-default',
31052                         html : '<i class="fa fa-repeat"></i>'
31053                     }
31054                 ]
31055             }
31056         ],
31057         ROTATOR : [
31058             {
31059                 tag : 'div',
31060                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31061                 action : 'rotate-left',
31062                 cn : [
31063                     {
31064                         tag : 'button',
31065                         cls : 'btn btn-default',
31066                         html : '<i class="fa fa-undo"></i>'
31067                     }
31068                 ]
31069             },
31070             {
31071                 tag : 'div',
31072                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31073                 action : 'rotate-right',
31074                 cn : [
31075                     {
31076                         tag : 'button',
31077                         cls : 'btn btn-default',
31078                         html : '<i class="fa fa-repeat"></i>'
31079                     }
31080                 ]
31081             }
31082         ]
31083     }
31084 });
31085
31086 /*
31087 * Licence: LGPL
31088 */
31089
31090 /**
31091  * @class Roo.bootstrap.DocumentManager
31092  * @extends Roo.bootstrap.Component
31093  * Bootstrap DocumentManager class
31094  * @cfg {String} paramName default 'imageUpload'
31095  * @cfg {String} toolTipName default 'filename'
31096  * @cfg {String} method default POST
31097  * @cfg {String} url action url
31098  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31099  * @cfg {Boolean} multiple multiple upload default true
31100  * @cfg {Number} thumbSize default 300
31101  * @cfg {String} fieldLabel
31102  * @cfg {Number} labelWidth default 4
31103  * @cfg {String} labelAlign (left|top) default left
31104  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31105 * @cfg {Number} labellg set the width of label (1-12)
31106  * @cfg {Number} labelmd set the width of label (1-12)
31107  * @cfg {Number} labelsm set the width of label (1-12)
31108  * @cfg {Number} labelxs set the width of label (1-12)
31109  * 
31110  * @constructor
31111  * Create a new DocumentManager
31112  * @param {Object} config The config object
31113  */
31114
31115 Roo.bootstrap.DocumentManager = function(config){
31116     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31117     
31118     this.files = [];
31119     this.delegates = [];
31120     
31121     this.addEvents({
31122         /**
31123          * @event initial
31124          * Fire when initial the DocumentManager
31125          * @param {Roo.bootstrap.DocumentManager} this
31126          */
31127         "initial" : true,
31128         /**
31129          * @event inspect
31130          * inspect selected file
31131          * @param {Roo.bootstrap.DocumentManager} this
31132          * @param {File} file
31133          */
31134         "inspect" : true,
31135         /**
31136          * @event exception
31137          * Fire when xhr load exception
31138          * @param {Roo.bootstrap.DocumentManager} this
31139          * @param {XMLHttpRequest} xhr
31140          */
31141         "exception" : true,
31142         /**
31143          * @event afterupload
31144          * Fire when xhr load exception
31145          * @param {Roo.bootstrap.DocumentManager} this
31146          * @param {XMLHttpRequest} xhr
31147          */
31148         "afterupload" : true,
31149         /**
31150          * @event prepare
31151          * prepare the form data
31152          * @param {Roo.bootstrap.DocumentManager} this
31153          * @param {Object} formData
31154          */
31155         "prepare" : true,
31156         /**
31157          * @event remove
31158          * Fire when remove the file
31159          * @param {Roo.bootstrap.DocumentManager} this
31160          * @param {Object} file
31161          */
31162         "remove" : true,
31163         /**
31164          * @event refresh
31165          * Fire after refresh the file
31166          * @param {Roo.bootstrap.DocumentManager} this
31167          */
31168         "refresh" : true,
31169         /**
31170          * @event click
31171          * Fire after click the image
31172          * @param {Roo.bootstrap.DocumentManager} this
31173          * @param {Object} file
31174          */
31175         "click" : true,
31176         /**
31177          * @event edit
31178          * Fire when upload a image and editable set to true
31179          * @param {Roo.bootstrap.DocumentManager} this
31180          * @param {Object} file
31181          */
31182         "edit" : true,
31183         /**
31184          * @event beforeselectfile
31185          * Fire before select file
31186          * @param {Roo.bootstrap.DocumentManager} this
31187          */
31188         "beforeselectfile" : true,
31189         /**
31190          * @event process
31191          * Fire before process file
31192          * @param {Roo.bootstrap.DocumentManager} this
31193          * @param {Object} file
31194          */
31195         "process" : true,
31196         /**
31197          * @event previewrendered
31198          * Fire when preview rendered
31199          * @param {Roo.bootstrap.DocumentManager} this
31200          * @param {Object} file
31201          */
31202         "previewrendered" : true,
31203         /**
31204          */
31205         "previewResize" : true
31206         
31207     });
31208 };
31209
31210 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31211     
31212     boxes : 0,
31213     inputName : '',
31214     thumbSize : 300,
31215     multiple : true,
31216     files : false,
31217     method : 'POST',
31218     url : '',
31219     paramName : 'imageUpload',
31220     toolTipName : 'filename',
31221     fieldLabel : '',
31222     labelWidth : 4,
31223     labelAlign : 'left',
31224     editable : true,
31225     delegates : false,
31226     xhr : false, 
31227     
31228     labellg : 0,
31229     labelmd : 0,
31230     labelsm : 0,
31231     labelxs : 0,
31232     
31233     getAutoCreate : function()
31234     {   
31235         var managerWidget = {
31236             tag : 'div',
31237             cls : 'roo-document-manager',
31238             cn : [
31239                 {
31240                     tag : 'input',
31241                     cls : 'roo-document-manager-selector',
31242                     type : 'file'
31243                 },
31244                 {
31245                     tag : 'div',
31246                     cls : 'roo-document-manager-uploader',
31247                     cn : [
31248                         {
31249                             tag : 'div',
31250                             cls : 'roo-document-manager-upload-btn',
31251                             html : '<i class="fa fa-plus"></i>'
31252                         }
31253                     ]
31254                     
31255                 }
31256             ]
31257         };
31258         
31259         var content = [
31260             {
31261                 tag : 'div',
31262                 cls : 'column col-md-12',
31263                 cn : managerWidget
31264             }
31265         ];
31266         
31267         if(this.fieldLabel.length){
31268             
31269             content = [
31270                 {
31271                     tag : 'div',
31272                     cls : 'column col-md-12',
31273                     html : this.fieldLabel
31274                 },
31275                 {
31276                     tag : 'div',
31277                     cls : 'column col-md-12',
31278                     cn : managerWidget
31279                 }
31280             ];
31281
31282             if(this.labelAlign == 'left'){
31283                 content = [
31284                     {
31285                         tag : 'div',
31286                         cls : 'column',
31287                         html : this.fieldLabel
31288                     },
31289                     {
31290                         tag : 'div',
31291                         cls : 'column',
31292                         cn : managerWidget
31293                     }
31294                 ];
31295                 
31296                 if(this.labelWidth > 12){
31297                     content[0].style = "width: " + this.labelWidth + 'px';
31298                 }
31299
31300                 if(this.labelWidth < 13 && this.labelmd == 0){
31301                     this.labelmd = this.labelWidth;
31302                 }
31303
31304                 if(this.labellg > 0){
31305                     content[0].cls += ' col-lg-' + this.labellg;
31306                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31307                 }
31308
31309                 if(this.labelmd > 0){
31310                     content[0].cls += ' col-md-' + this.labelmd;
31311                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31312                 }
31313
31314                 if(this.labelsm > 0){
31315                     content[0].cls += ' col-sm-' + this.labelsm;
31316                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31317                 }
31318
31319                 if(this.labelxs > 0){
31320                     content[0].cls += ' col-xs-' + this.labelxs;
31321                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31322                 }
31323                 
31324             }
31325         }
31326         
31327         var cfg = {
31328             tag : 'div',
31329             cls : 'row clearfix',
31330             cn : content
31331         };
31332         
31333         return cfg;
31334         
31335     },
31336     
31337     initEvents : function()
31338     {
31339         this.managerEl = this.el.select('.roo-document-manager', true).first();
31340         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31341         
31342         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31343         this.selectorEl.hide();
31344         
31345         if(this.multiple){
31346             this.selectorEl.attr('multiple', 'multiple');
31347         }
31348         
31349         this.selectorEl.on('change', this.onFileSelected, this);
31350         
31351         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31352         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31353         
31354         this.uploader.on('click', this.onUploaderClick, this);
31355         
31356         this.renderProgressDialog();
31357         
31358         var _this = this;
31359         
31360         window.addEventListener("resize", function() { _this.refresh(); } );
31361         
31362         this.fireEvent('initial', this);
31363     },
31364     
31365     renderProgressDialog : function()
31366     {
31367         var _this = this;
31368         
31369         this.progressDialog = new Roo.bootstrap.Modal({
31370             cls : 'roo-document-manager-progress-dialog',
31371             allow_close : false,
31372             animate : false,
31373             title : '',
31374             buttons : [
31375                 {
31376                     name  :'cancel',
31377                     weight : 'danger',
31378                     html : 'Cancel'
31379                 }
31380             ], 
31381             listeners : { 
31382                 btnclick : function() {
31383                     _this.uploadCancel();
31384                     this.hide();
31385                 }
31386             }
31387         });
31388          
31389         this.progressDialog.render(Roo.get(document.body));
31390          
31391         this.progress = new Roo.bootstrap.Progress({
31392             cls : 'roo-document-manager-progress',
31393             active : true,
31394             striped : true
31395         });
31396         
31397         this.progress.render(this.progressDialog.getChildContainer());
31398         
31399         this.progressBar = new Roo.bootstrap.ProgressBar({
31400             cls : 'roo-document-manager-progress-bar',
31401             aria_valuenow : 0,
31402             aria_valuemin : 0,
31403             aria_valuemax : 12,
31404             panel : 'success'
31405         });
31406         
31407         this.progressBar.render(this.progress.getChildContainer());
31408     },
31409     
31410     onUploaderClick : function(e)
31411     {
31412         e.preventDefault();
31413      
31414         if(this.fireEvent('beforeselectfile', this) != false){
31415             this.selectorEl.dom.click();
31416         }
31417         
31418     },
31419     
31420     onFileSelected : function(e)
31421     {
31422         e.preventDefault();
31423         
31424         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31425             return;
31426         }
31427         
31428         Roo.each(this.selectorEl.dom.files, function(file){
31429             if(this.fireEvent('inspect', this, file) != false){
31430                 this.files.push(file);
31431             }
31432         }, this);
31433         
31434         this.queue();
31435         
31436     },
31437     
31438     queue : function()
31439     {
31440         this.selectorEl.dom.value = '';
31441         
31442         if(!this.files || !this.files.length){
31443             return;
31444         }
31445         
31446         if(this.boxes > 0 && this.files.length > this.boxes){
31447             this.files = this.files.slice(0, this.boxes);
31448         }
31449         
31450         this.uploader.show();
31451         
31452         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31453             this.uploader.hide();
31454         }
31455         
31456         var _this = this;
31457         
31458         var files = [];
31459         
31460         var docs = [];
31461         
31462         Roo.each(this.files, function(file){
31463             
31464             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31465                 var f = this.renderPreview(file);
31466                 files.push(f);
31467                 return;
31468             }
31469             
31470             if(file.type.indexOf('image') != -1){
31471                 this.delegates.push(
31472                     (function(){
31473                         _this.process(file);
31474                     }).createDelegate(this)
31475                 );
31476         
31477                 return;
31478             }
31479             
31480             docs.push(
31481                 (function(){
31482                     _this.process(file);
31483                 }).createDelegate(this)
31484             );
31485             
31486         }, this);
31487         
31488         this.files = files;
31489         
31490         this.delegates = this.delegates.concat(docs);
31491         
31492         if(!this.delegates.length){
31493             this.refresh();
31494             return;
31495         }
31496         
31497         this.progressBar.aria_valuemax = this.delegates.length;
31498         
31499         this.arrange();
31500         
31501         return;
31502     },
31503     
31504     arrange : function()
31505     {
31506         if(!this.delegates.length){
31507             this.progressDialog.hide();
31508             this.refresh();
31509             return;
31510         }
31511         
31512         var delegate = this.delegates.shift();
31513         
31514         this.progressDialog.show();
31515         
31516         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31517         
31518         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31519         
31520         delegate();
31521     },
31522     
31523     refresh : function()
31524     {
31525         this.uploader.show();
31526         
31527         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31528             this.uploader.hide();
31529         }
31530         
31531         Roo.isTouch ? this.closable(false) : this.closable(true);
31532         
31533         this.fireEvent('refresh', this);
31534     },
31535     
31536     onRemove : function(e, el, o)
31537     {
31538         e.preventDefault();
31539         
31540         this.fireEvent('remove', this, o);
31541         
31542     },
31543     
31544     remove : function(o)
31545     {
31546         var files = [];
31547         
31548         Roo.each(this.files, function(file){
31549             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31550                 files.push(file);
31551                 return;
31552             }
31553
31554             o.target.remove();
31555
31556         }, this);
31557         
31558         this.files = files;
31559         
31560         this.refresh();
31561     },
31562     
31563     clear : function()
31564     {
31565         Roo.each(this.files, function(file){
31566             if(!file.target){
31567                 return;
31568             }
31569             
31570             file.target.remove();
31571
31572         }, this);
31573         
31574         this.files = [];
31575         
31576         this.refresh();
31577     },
31578     
31579     onClick : function(e, el, o)
31580     {
31581         e.preventDefault();
31582         
31583         this.fireEvent('click', this, o);
31584         
31585     },
31586     
31587     closable : function(closable)
31588     {
31589         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31590             
31591             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31592             
31593             if(closable){
31594                 el.show();
31595                 return;
31596             }
31597             
31598             el.hide();
31599             
31600         }, this);
31601     },
31602     
31603     xhrOnLoad : function(xhr)
31604     {
31605         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31606             el.remove();
31607         }, this);
31608         
31609         if (xhr.readyState !== 4) {
31610             this.arrange();
31611             this.fireEvent('exception', this, xhr);
31612             return;
31613         }
31614
31615         var response = Roo.decode(xhr.responseText);
31616         
31617         if(!response.success){
31618             this.arrange();
31619             this.fireEvent('exception', this, xhr);
31620             return;
31621         }
31622         
31623         var file = this.renderPreview(response.data);
31624         
31625         this.files.push(file);
31626         
31627         this.arrange();
31628         
31629         this.fireEvent('afterupload', this, xhr);
31630         
31631     },
31632     
31633     xhrOnError : function(xhr)
31634     {
31635         Roo.log('xhr on error');
31636         
31637         var response = Roo.decode(xhr.responseText);
31638           
31639         Roo.log(response);
31640         
31641         this.arrange();
31642     },
31643     
31644     process : function(file)
31645     {
31646         if(this.fireEvent('process', this, file) !== false){
31647             if(this.editable && file.type.indexOf('image') != -1){
31648                 this.fireEvent('edit', this, file);
31649                 return;
31650             }
31651
31652             this.uploadStart(file, false);
31653
31654             return;
31655         }
31656         
31657     },
31658     
31659     uploadStart : function(file, crop)
31660     {
31661         this.xhr = new XMLHttpRequest();
31662         
31663         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31664             this.arrange();
31665             return;
31666         }
31667         
31668         file.xhr = this.xhr;
31669             
31670         this.managerEl.createChild({
31671             tag : 'div',
31672             cls : 'roo-document-manager-loading',
31673             cn : [
31674                 {
31675                     tag : 'div',
31676                     tooltip : file.name,
31677                     cls : 'roo-document-manager-thumb',
31678                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31679                 }
31680             ]
31681
31682         });
31683
31684         this.xhr.open(this.method, this.url, true);
31685         
31686         var headers = {
31687             "Accept": "application/json",
31688             "Cache-Control": "no-cache",
31689             "X-Requested-With": "XMLHttpRequest"
31690         };
31691         
31692         for (var headerName in headers) {
31693             var headerValue = headers[headerName];
31694             if (headerValue) {
31695                 this.xhr.setRequestHeader(headerName, headerValue);
31696             }
31697         }
31698         
31699         var _this = this;
31700         
31701         this.xhr.onload = function()
31702         {
31703             _this.xhrOnLoad(_this.xhr);
31704         }
31705         
31706         this.xhr.onerror = function()
31707         {
31708             _this.xhrOnError(_this.xhr);
31709         }
31710         
31711         var formData = new FormData();
31712
31713         formData.append('returnHTML', 'NO');
31714         
31715         if(crop){
31716             formData.append('crop', crop);
31717         }
31718         
31719         formData.append(this.paramName, file, file.name);
31720         
31721         var options = {
31722             file : file, 
31723             manually : false
31724         };
31725         
31726         if(this.fireEvent('prepare', this, formData, options) != false){
31727             
31728             if(options.manually){
31729                 return;
31730             }
31731             
31732             this.xhr.send(formData);
31733             return;
31734         };
31735         
31736         this.uploadCancel();
31737     },
31738     
31739     uploadCancel : function()
31740     {
31741         if (this.xhr) {
31742             this.xhr.abort();
31743         }
31744         
31745         this.delegates = [];
31746         
31747         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31748             el.remove();
31749         }, this);
31750         
31751         this.arrange();
31752     },
31753     
31754     renderPreview : function(file)
31755     {
31756         if(typeof(file.target) != 'undefined' && file.target){
31757             return file;
31758         }
31759         
31760         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31761         
31762         var previewEl = this.managerEl.createChild({
31763             tag : 'div',
31764             cls : 'roo-document-manager-preview',
31765             cn : [
31766                 {
31767                     tag : 'div',
31768                     tooltip : file[this.toolTipName],
31769                     cls : 'roo-document-manager-thumb',
31770                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31771                 },
31772                 {
31773                     tag : 'button',
31774                     cls : 'close',
31775                     html : '<i class="fa fa-times-circle"></i>'
31776                 }
31777             ]
31778         });
31779
31780         var close = previewEl.select('button.close', true).first();
31781
31782         close.on('click', this.onRemove, this, file);
31783
31784         file.target = previewEl;
31785
31786         var image = previewEl.select('img', true).first();
31787         
31788         var _this = this;
31789         
31790         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31791         
31792         image.on('click', this.onClick, this, file);
31793         
31794         this.fireEvent('previewrendered', this, file);
31795         
31796         return file;
31797         
31798     },
31799     
31800     onPreviewLoad : function(file, image)
31801     {
31802         if(typeof(file.target) == 'undefined' || !file.target){
31803             return;
31804         }
31805         
31806         var width = image.dom.naturalWidth || image.dom.width;
31807         var height = image.dom.naturalHeight || image.dom.height;
31808         
31809         if(!this.previewResize) {
31810             return;
31811         }
31812         
31813         if(width > height){
31814             file.target.addClass('wide');
31815             return;
31816         }
31817         
31818         file.target.addClass('tall');
31819         return;
31820         
31821     },
31822     
31823     uploadFromSource : function(file, crop)
31824     {
31825         this.xhr = new XMLHttpRequest();
31826         
31827         this.managerEl.createChild({
31828             tag : 'div',
31829             cls : 'roo-document-manager-loading',
31830             cn : [
31831                 {
31832                     tag : 'div',
31833                     tooltip : file.name,
31834                     cls : 'roo-document-manager-thumb',
31835                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31836                 }
31837             ]
31838
31839         });
31840
31841         this.xhr.open(this.method, this.url, true);
31842         
31843         var headers = {
31844             "Accept": "application/json",
31845             "Cache-Control": "no-cache",
31846             "X-Requested-With": "XMLHttpRequest"
31847         };
31848         
31849         for (var headerName in headers) {
31850             var headerValue = headers[headerName];
31851             if (headerValue) {
31852                 this.xhr.setRequestHeader(headerName, headerValue);
31853             }
31854         }
31855         
31856         var _this = this;
31857         
31858         this.xhr.onload = function()
31859         {
31860             _this.xhrOnLoad(_this.xhr);
31861         }
31862         
31863         this.xhr.onerror = function()
31864         {
31865             _this.xhrOnError(_this.xhr);
31866         }
31867         
31868         var formData = new FormData();
31869
31870         formData.append('returnHTML', 'NO');
31871         
31872         formData.append('crop', crop);
31873         
31874         if(typeof(file.filename) != 'undefined'){
31875             formData.append('filename', file.filename);
31876         }
31877         
31878         if(typeof(file.mimetype) != 'undefined'){
31879             formData.append('mimetype', file.mimetype);
31880         }
31881         
31882         Roo.log(formData);
31883         
31884         if(this.fireEvent('prepare', this, formData) != false){
31885             this.xhr.send(formData);
31886         };
31887     }
31888 });
31889
31890 /*
31891 * Licence: LGPL
31892 */
31893
31894 /**
31895  * @class Roo.bootstrap.DocumentViewer
31896  * @extends Roo.bootstrap.Component
31897  * Bootstrap DocumentViewer class
31898  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31899  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31900  * 
31901  * @constructor
31902  * Create a new DocumentViewer
31903  * @param {Object} config The config object
31904  */
31905
31906 Roo.bootstrap.DocumentViewer = function(config){
31907     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31908     
31909     this.addEvents({
31910         /**
31911          * @event initial
31912          * Fire after initEvent
31913          * @param {Roo.bootstrap.DocumentViewer} this
31914          */
31915         "initial" : true,
31916         /**
31917          * @event click
31918          * Fire after click
31919          * @param {Roo.bootstrap.DocumentViewer} this
31920          */
31921         "click" : true,
31922         /**
31923          * @event download
31924          * Fire after download button
31925          * @param {Roo.bootstrap.DocumentViewer} this
31926          */
31927         "download" : true,
31928         /**
31929          * @event trash
31930          * Fire after trash button
31931          * @param {Roo.bootstrap.DocumentViewer} this
31932          */
31933         "trash" : true
31934         
31935     });
31936 };
31937
31938 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31939     
31940     showDownload : true,
31941     
31942     showTrash : true,
31943     
31944     getAutoCreate : function()
31945     {
31946         var cfg = {
31947             tag : 'div',
31948             cls : 'roo-document-viewer',
31949             cn : [
31950                 {
31951                     tag : 'div',
31952                     cls : 'roo-document-viewer-body',
31953                     cn : [
31954                         {
31955                             tag : 'div',
31956                             cls : 'roo-document-viewer-thumb',
31957                             cn : [
31958                                 {
31959                                     tag : 'img',
31960                                     cls : 'roo-document-viewer-image'
31961                                 }
31962                             ]
31963                         }
31964                     ]
31965                 },
31966                 {
31967                     tag : 'div',
31968                     cls : 'roo-document-viewer-footer',
31969                     cn : {
31970                         tag : 'div',
31971                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31972                         cn : [
31973                             {
31974                                 tag : 'div',
31975                                 cls : 'btn-group roo-document-viewer-download',
31976                                 cn : [
31977                                     {
31978                                         tag : 'button',
31979                                         cls : 'btn btn-default',
31980                                         html : '<i class="fa fa-download"></i>'
31981                                     }
31982                                 ]
31983                             },
31984                             {
31985                                 tag : 'div',
31986                                 cls : 'btn-group roo-document-viewer-trash',
31987                                 cn : [
31988                                     {
31989                                         tag : 'button',
31990                                         cls : 'btn btn-default',
31991                                         html : '<i class="fa fa-trash"></i>'
31992                                     }
31993                                 ]
31994                             }
31995                         ]
31996                     }
31997                 }
31998             ]
31999         };
32000         
32001         return cfg;
32002     },
32003     
32004     initEvents : function()
32005     {
32006         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32007         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32008         
32009         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32010         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32011         
32012         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32013         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32014         
32015         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32016         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32017         
32018         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32019         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32020         
32021         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32022         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32023         
32024         this.bodyEl.on('click', this.onClick, this);
32025         this.downloadBtn.on('click', this.onDownload, this);
32026         this.trashBtn.on('click', this.onTrash, this);
32027         
32028         this.downloadBtn.hide();
32029         this.trashBtn.hide();
32030         
32031         if(this.showDownload){
32032             this.downloadBtn.show();
32033         }
32034         
32035         if(this.showTrash){
32036             this.trashBtn.show();
32037         }
32038         
32039         if(!this.showDownload && !this.showTrash) {
32040             this.footerEl.hide();
32041         }
32042         
32043     },
32044     
32045     initial : function()
32046     {
32047         this.fireEvent('initial', this);
32048         
32049     },
32050     
32051     onClick : function(e)
32052     {
32053         e.preventDefault();
32054         
32055         this.fireEvent('click', this);
32056     },
32057     
32058     onDownload : function(e)
32059     {
32060         e.preventDefault();
32061         
32062         this.fireEvent('download', this);
32063     },
32064     
32065     onTrash : function(e)
32066     {
32067         e.preventDefault();
32068         
32069         this.fireEvent('trash', this);
32070     }
32071     
32072 });
32073 /*
32074  * - LGPL
32075  *
32076  * nav progress bar
32077  * 
32078  */
32079
32080 /**
32081  * @class Roo.bootstrap.NavProgressBar
32082  * @extends Roo.bootstrap.Component
32083  * Bootstrap NavProgressBar class
32084  * 
32085  * @constructor
32086  * Create a new nav progress bar
32087  * @param {Object} config The config object
32088  */
32089
32090 Roo.bootstrap.NavProgressBar = function(config){
32091     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32092
32093     this.bullets = this.bullets || [];
32094    
32095 //    Roo.bootstrap.NavProgressBar.register(this);
32096      this.addEvents({
32097         /**
32098              * @event changed
32099              * Fires when the active item changes
32100              * @param {Roo.bootstrap.NavProgressBar} this
32101              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32102              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32103          */
32104         'changed': true
32105      });
32106     
32107 };
32108
32109 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32110     
32111     bullets : [],
32112     barItems : [],
32113     
32114     getAutoCreate : function()
32115     {
32116         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32117         
32118         cfg = {
32119             tag : 'div',
32120             cls : 'roo-navigation-bar-group',
32121             cn : [
32122                 {
32123                     tag : 'div',
32124                     cls : 'roo-navigation-top-bar'
32125                 },
32126                 {
32127                     tag : 'div',
32128                     cls : 'roo-navigation-bullets-bar',
32129                     cn : [
32130                         {
32131                             tag : 'ul',
32132                             cls : 'roo-navigation-bar'
32133                         }
32134                     ]
32135                 },
32136                 
32137                 {
32138                     tag : 'div',
32139                     cls : 'roo-navigation-bottom-bar'
32140                 }
32141             ]
32142             
32143         };
32144         
32145         return cfg;
32146         
32147     },
32148     
32149     initEvents: function() 
32150     {
32151         
32152     },
32153     
32154     onRender : function(ct, position) 
32155     {
32156         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32157         
32158         if(this.bullets.length){
32159             Roo.each(this.bullets, function(b){
32160                this.addItem(b);
32161             }, this);
32162         }
32163         
32164         this.format();
32165         
32166     },
32167     
32168     addItem : function(cfg)
32169     {
32170         var item = new Roo.bootstrap.NavProgressItem(cfg);
32171         
32172         item.parentId = this.id;
32173         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32174         
32175         if(cfg.html){
32176             var top = new Roo.bootstrap.Element({
32177                 tag : 'div',
32178                 cls : 'roo-navigation-bar-text'
32179             });
32180             
32181             var bottom = new Roo.bootstrap.Element({
32182                 tag : 'div',
32183                 cls : 'roo-navigation-bar-text'
32184             });
32185             
32186             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32187             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32188             
32189             var topText = new Roo.bootstrap.Element({
32190                 tag : 'span',
32191                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32192             });
32193             
32194             var bottomText = new Roo.bootstrap.Element({
32195                 tag : 'span',
32196                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32197             });
32198             
32199             topText.onRender(top.el, null);
32200             bottomText.onRender(bottom.el, null);
32201             
32202             item.topEl = top;
32203             item.bottomEl = bottom;
32204         }
32205         
32206         this.barItems.push(item);
32207         
32208         return item;
32209     },
32210     
32211     getActive : function()
32212     {
32213         var active = false;
32214         
32215         Roo.each(this.barItems, function(v){
32216             
32217             if (!v.isActive()) {
32218                 return;
32219             }
32220             
32221             active = v;
32222             return false;
32223             
32224         });
32225         
32226         return active;
32227     },
32228     
32229     setActiveItem : function(item)
32230     {
32231         var prev = false;
32232         
32233         Roo.each(this.barItems, function(v){
32234             if (v.rid == item.rid) {
32235                 return ;
32236             }
32237             
32238             if (v.isActive()) {
32239                 v.setActive(false);
32240                 prev = v;
32241             }
32242         });
32243
32244         item.setActive(true);
32245         
32246         this.fireEvent('changed', this, item, prev);
32247     },
32248     
32249     getBarItem: function(rid)
32250     {
32251         var ret = false;
32252         
32253         Roo.each(this.barItems, function(e) {
32254             if (e.rid != rid) {
32255                 return;
32256             }
32257             
32258             ret =  e;
32259             return false;
32260         });
32261         
32262         return ret;
32263     },
32264     
32265     indexOfItem : function(item)
32266     {
32267         var index = false;
32268         
32269         Roo.each(this.barItems, function(v, i){
32270             
32271             if (v.rid != item.rid) {
32272                 return;
32273             }
32274             
32275             index = i;
32276             return false
32277         });
32278         
32279         return index;
32280     },
32281     
32282     setActiveNext : function()
32283     {
32284         var i = this.indexOfItem(this.getActive());
32285         
32286         if (i > this.barItems.length) {
32287             return;
32288         }
32289         
32290         this.setActiveItem(this.barItems[i+1]);
32291     },
32292     
32293     setActivePrev : function()
32294     {
32295         var i = this.indexOfItem(this.getActive());
32296         
32297         if (i  < 1) {
32298             return;
32299         }
32300         
32301         this.setActiveItem(this.barItems[i-1]);
32302     },
32303     
32304     format : function()
32305     {
32306         if(!this.barItems.length){
32307             return;
32308         }
32309      
32310         var width = 100 / this.barItems.length;
32311         
32312         Roo.each(this.barItems, function(i){
32313             i.el.setStyle('width', width + '%');
32314             i.topEl.el.setStyle('width', width + '%');
32315             i.bottomEl.el.setStyle('width', width + '%');
32316         }, this);
32317         
32318     }
32319     
32320 });
32321 /*
32322  * - LGPL
32323  *
32324  * Nav Progress Item
32325  * 
32326  */
32327
32328 /**
32329  * @class Roo.bootstrap.NavProgressItem
32330  * @extends Roo.bootstrap.Component
32331  * Bootstrap NavProgressItem class
32332  * @cfg {String} rid the reference id
32333  * @cfg {Boolean} active (true|false) Is item active default false
32334  * @cfg {Boolean} disabled (true|false) Is item active default false
32335  * @cfg {String} html
32336  * @cfg {String} position (top|bottom) text position default bottom
32337  * @cfg {String} icon show icon instead of number
32338  * 
32339  * @constructor
32340  * Create a new NavProgressItem
32341  * @param {Object} config The config object
32342  */
32343 Roo.bootstrap.NavProgressItem = function(config){
32344     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32345     this.addEvents({
32346         // raw events
32347         /**
32348          * @event click
32349          * The raw click event for the entire grid.
32350          * @param {Roo.bootstrap.NavProgressItem} this
32351          * @param {Roo.EventObject} e
32352          */
32353         "click" : true
32354     });
32355    
32356 };
32357
32358 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32359     
32360     rid : '',
32361     active : false,
32362     disabled : false,
32363     html : '',
32364     position : 'bottom',
32365     icon : false,
32366     
32367     getAutoCreate : function()
32368     {
32369         var iconCls = 'roo-navigation-bar-item-icon';
32370         
32371         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32372         
32373         var cfg = {
32374             tag: 'li',
32375             cls: 'roo-navigation-bar-item',
32376             cn : [
32377                 {
32378                     tag : 'i',
32379                     cls : iconCls
32380                 }
32381             ]
32382         };
32383         
32384         if(this.active){
32385             cfg.cls += ' active';
32386         }
32387         if(this.disabled){
32388             cfg.cls += ' disabled';
32389         }
32390         
32391         return cfg;
32392     },
32393     
32394     disable : function()
32395     {
32396         this.setDisabled(true);
32397     },
32398     
32399     enable : function()
32400     {
32401         this.setDisabled(false);
32402     },
32403     
32404     initEvents: function() 
32405     {
32406         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32407         
32408         this.iconEl.on('click', this.onClick, this);
32409     },
32410     
32411     onClick : function(e)
32412     {
32413         e.preventDefault();
32414         
32415         if(this.disabled){
32416             return;
32417         }
32418         
32419         if(this.fireEvent('click', this, e) === false){
32420             return;
32421         };
32422         
32423         this.parent().setActiveItem(this);
32424     },
32425     
32426     isActive: function () 
32427     {
32428         return this.active;
32429     },
32430     
32431     setActive : function(state)
32432     {
32433         if(this.active == state){
32434             return;
32435         }
32436         
32437         this.active = state;
32438         
32439         if (state) {
32440             this.el.addClass('active');
32441             return;
32442         }
32443         
32444         this.el.removeClass('active');
32445         
32446         return;
32447     },
32448     
32449     setDisabled : function(state)
32450     {
32451         if(this.disabled == state){
32452             return;
32453         }
32454         
32455         this.disabled = state;
32456         
32457         if (state) {
32458             this.el.addClass('disabled');
32459             return;
32460         }
32461         
32462         this.el.removeClass('disabled');
32463     },
32464     
32465     tooltipEl : function()
32466     {
32467         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32468     }
32469 });
32470  
32471
32472  /*
32473  * - LGPL
32474  *
32475  * FieldLabel
32476  * 
32477  */
32478
32479 /**
32480  * @class Roo.bootstrap.FieldLabel
32481  * @extends Roo.bootstrap.Component
32482  * Bootstrap FieldLabel class
32483  * @cfg {String} html contents of the element
32484  * @cfg {String} tag tag of the element default label
32485  * @cfg {String} cls class of the element
32486  * @cfg {String} target label target 
32487  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32488  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32489  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32490  * @cfg {String} iconTooltip default "This field is required"
32491  * @cfg {String} indicatorpos (left|right) default left
32492  * 
32493  * @constructor
32494  * Create a new FieldLabel
32495  * @param {Object} config The config object
32496  */
32497
32498 Roo.bootstrap.FieldLabel = function(config){
32499     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32500     
32501     this.addEvents({
32502             /**
32503              * @event invalid
32504              * Fires after the field has been marked as invalid.
32505              * @param {Roo.form.FieldLabel} this
32506              * @param {String} msg The validation message
32507              */
32508             invalid : true,
32509             /**
32510              * @event valid
32511              * Fires after the field has been validated with no errors.
32512              * @param {Roo.form.FieldLabel} this
32513              */
32514             valid : true
32515         });
32516 };
32517
32518 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32519     
32520     tag: 'label',
32521     cls: '',
32522     html: '',
32523     target: '',
32524     allowBlank : true,
32525     invalidClass : 'has-warning',
32526     validClass : 'has-success',
32527     iconTooltip : 'This field is required',
32528     indicatorpos : 'left',
32529     
32530     getAutoCreate : function(){
32531         
32532         var cls = "";
32533         if (!this.allowBlank) {
32534             cls  = "visible";
32535         }
32536         
32537         var cfg = {
32538             tag : this.tag,
32539             cls : 'roo-bootstrap-field-label ' + this.cls,
32540             for : this.target,
32541             cn : [
32542                 {
32543                     tag : 'i',
32544                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32545                     tooltip : this.iconTooltip
32546                 },
32547                 {
32548                     tag : 'span',
32549                     html : this.html
32550                 }
32551             ] 
32552         };
32553         
32554         if(this.indicatorpos == 'right'){
32555             var cfg = {
32556                 tag : this.tag,
32557                 cls : 'roo-bootstrap-field-label ' + this.cls,
32558                 for : this.target,
32559                 cn : [
32560                     {
32561                         tag : 'span',
32562                         html : this.html
32563                     },
32564                     {
32565                         tag : 'i',
32566                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32567                         tooltip : this.iconTooltip
32568                     }
32569                 ] 
32570             };
32571         }
32572         
32573         return cfg;
32574     },
32575     
32576     initEvents: function() 
32577     {
32578         Roo.bootstrap.Element.superclass.initEvents.call(this);
32579         
32580         this.indicator = this.indicatorEl();
32581         
32582         if(this.indicator){
32583             this.indicator.removeClass('visible');
32584             this.indicator.addClass('invisible');
32585         }
32586         
32587         Roo.bootstrap.FieldLabel.register(this);
32588     },
32589     
32590     indicatorEl : function()
32591     {
32592         var indicator = this.el.select('i.roo-required-indicator',true).first();
32593         
32594         if(!indicator){
32595             return false;
32596         }
32597         
32598         return indicator;
32599         
32600     },
32601     
32602     /**
32603      * Mark this field as valid
32604      */
32605     markValid : function()
32606     {
32607         if(this.indicator){
32608             this.indicator.removeClass('visible');
32609             this.indicator.addClass('invisible');
32610         }
32611         if (Roo.bootstrap.version == 3) {
32612             this.el.removeClass(this.invalidClass);
32613             this.el.addClass(this.validClass);
32614         } else {
32615             this.el.removeClass('is-invalid');
32616             this.el.addClass('is-valid');
32617         }
32618         
32619         
32620         this.fireEvent('valid', this);
32621     },
32622     
32623     /**
32624      * Mark this field as invalid
32625      * @param {String} msg The validation message
32626      */
32627     markInvalid : function(msg)
32628     {
32629         if(this.indicator){
32630             this.indicator.removeClass('invisible');
32631             this.indicator.addClass('visible');
32632         }
32633           if (Roo.bootstrap.version == 3) {
32634             this.el.removeClass(this.validClass);
32635             this.el.addClass(this.invalidClass);
32636         } else {
32637             this.el.removeClass('is-valid');
32638             this.el.addClass('is-invalid');
32639         }
32640         
32641         
32642         this.fireEvent('invalid', this, msg);
32643     }
32644     
32645    
32646 });
32647
32648 Roo.apply(Roo.bootstrap.FieldLabel, {
32649     
32650     groups: {},
32651     
32652      /**
32653     * register a FieldLabel Group
32654     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32655     */
32656     register : function(label)
32657     {
32658         if(this.groups.hasOwnProperty(label.target)){
32659             return;
32660         }
32661      
32662         this.groups[label.target] = label;
32663         
32664     },
32665     /**
32666     * fetch a FieldLabel Group based on the target
32667     * @param {string} target
32668     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32669     */
32670     get: function(target) {
32671         if (typeof(this.groups[target]) == 'undefined') {
32672             return false;
32673         }
32674         
32675         return this.groups[target] ;
32676     }
32677 });
32678
32679  
32680
32681  /*
32682  * - LGPL
32683  *
32684  * page DateSplitField.
32685  * 
32686  */
32687
32688
32689 /**
32690  * @class Roo.bootstrap.DateSplitField
32691  * @extends Roo.bootstrap.Component
32692  * Bootstrap DateSplitField class
32693  * @cfg {string} fieldLabel - the label associated
32694  * @cfg {Number} labelWidth set the width of label (0-12)
32695  * @cfg {String} labelAlign (top|left)
32696  * @cfg {Boolean} dayAllowBlank (true|false) default false
32697  * @cfg {Boolean} monthAllowBlank (true|false) default false
32698  * @cfg {Boolean} yearAllowBlank (true|false) default false
32699  * @cfg {string} dayPlaceholder 
32700  * @cfg {string} monthPlaceholder
32701  * @cfg {string} yearPlaceholder
32702  * @cfg {string} dayFormat default 'd'
32703  * @cfg {string} monthFormat default 'm'
32704  * @cfg {string} yearFormat default 'Y'
32705  * @cfg {Number} labellg set the width of label (1-12)
32706  * @cfg {Number} labelmd set the width of label (1-12)
32707  * @cfg {Number} labelsm set the width of label (1-12)
32708  * @cfg {Number} labelxs set the width of label (1-12)
32709
32710  *     
32711  * @constructor
32712  * Create a new DateSplitField
32713  * @param {Object} config The config object
32714  */
32715
32716 Roo.bootstrap.DateSplitField = function(config){
32717     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32718     
32719     this.addEvents({
32720         // raw events
32721          /**
32722          * @event years
32723          * getting the data of years
32724          * @param {Roo.bootstrap.DateSplitField} this
32725          * @param {Object} years
32726          */
32727         "years" : true,
32728         /**
32729          * @event days
32730          * getting the data of days
32731          * @param {Roo.bootstrap.DateSplitField} this
32732          * @param {Object} days
32733          */
32734         "days" : true,
32735         /**
32736          * @event invalid
32737          * Fires after the field has been marked as invalid.
32738          * @param {Roo.form.Field} this
32739          * @param {String} msg The validation message
32740          */
32741         invalid : true,
32742        /**
32743          * @event valid
32744          * Fires after the field has been validated with no errors.
32745          * @param {Roo.form.Field} this
32746          */
32747         valid : true
32748     });
32749 };
32750
32751 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32752     
32753     fieldLabel : '',
32754     labelAlign : 'top',
32755     labelWidth : 3,
32756     dayAllowBlank : false,
32757     monthAllowBlank : false,
32758     yearAllowBlank : false,
32759     dayPlaceholder : '',
32760     monthPlaceholder : '',
32761     yearPlaceholder : '',
32762     dayFormat : 'd',
32763     monthFormat : 'm',
32764     yearFormat : 'Y',
32765     isFormField : true,
32766     labellg : 0,
32767     labelmd : 0,
32768     labelsm : 0,
32769     labelxs : 0,
32770     
32771     getAutoCreate : function()
32772     {
32773         var cfg = {
32774             tag : 'div',
32775             cls : 'row roo-date-split-field-group',
32776             cn : [
32777                 {
32778                     tag : 'input',
32779                     type : 'hidden',
32780                     cls : 'form-hidden-field roo-date-split-field-group-value',
32781                     name : this.name
32782                 }
32783             ]
32784         };
32785         
32786         var labelCls = 'col-md-12';
32787         var contentCls = 'col-md-4';
32788         
32789         if(this.fieldLabel){
32790             
32791             var label = {
32792                 tag : 'div',
32793                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32794                 cn : [
32795                     {
32796                         tag : 'label',
32797                         html : this.fieldLabel
32798                     }
32799                 ]
32800             };
32801             
32802             if(this.labelAlign == 'left'){
32803             
32804                 if(this.labelWidth > 12){
32805                     label.style = "width: " + this.labelWidth + 'px';
32806                 }
32807
32808                 if(this.labelWidth < 13 && this.labelmd == 0){
32809                     this.labelmd = this.labelWidth;
32810                 }
32811
32812                 if(this.labellg > 0){
32813                     labelCls = ' col-lg-' + this.labellg;
32814                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32815                 }
32816
32817                 if(this.labelmd > 0){
32818                     labelCls = ' col-md-' + this.labelmd;
32819                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32820                 }
32821
32822                 if(this.labelsm > 0){
32823                     labelCls = ' col-sm-' + this.labelsm;
32824                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32825                 }
32826
32827                 if(this.labelxs > 0){
32828                     labelCls = ' col-xs-' + this.labelxs;
32829                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32830                 }
32831             }
32832             
32833             label.cls += ' ' + labelCls;
32834             
32835             cfg.cn.push(label);
32836         }
32837         
32838         Roo.each(['day', 'month', 'year'], function(t){
32839             cfg.cn.push({
32840                 tag : 'div',
32841                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32842             });
32843         }, this);
32844         
32845         return cfg;
32846     },
32847     
32848     inputEl: function ()
32849     {
32850         return this.el.select('.roo-date-split-field-group-value', true).first();
32851     },
32852     
32853     onRender : function(ct, position) 
32854     {
32855         var _this = this;
32856         
32857         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32858         
32859         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32860         
32861         this.dayField = new Roo.bootstrap.ComboBox({
32862             allowBlank : this.dayAllowBlank,
32863             alwaysQuery : true,
32864             displayField : 'value',
32865             editable : false,
32866             fieldLabel : '',
32867             forceSelection : true,
32868             mode : 'local',
32869             placeholder : this.dayPlaceholder,
32870             selectOnFocus : true,
32871             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32872             triggerAction : 'all',
32873             typeAhead : true,
32874             valueField : 'value',
32875             store : new Roo.data.SimpleStore({
32876                 data : (function() {    
32877                     var days = [];
32878                     _this.fireEvent('days', _this, days);
32879                     return days;
32880                 })(),
32881                 fields : [ 'value' ]
32882             }),
32883             listeners : {
32884                 select : function (_self, record, index)
32885                 {
32886                     _this.setValue(_this.getValue());
32887                 }
32888             }
32889         });
32890
32891         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32892         
32893         this.monthField = new Roo.bootstrap.MonthField({
32894             after : '<i class=\"fa fa-calendar\"></i>',
32895             allowBlank : this.monthAllowBlank,
32896             placeholder : this.monthPlaceholder,
32897             readOnly : true,
32898             listeners : {
32899                 render : function (_self)
32900                 {
32901                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32902                         e.preventDefault();
32903                         _self.focus();
32904                     });
32905                 },
32906                 select : function (_self, oldvalue, newvalue)
32907                 {
32908                     _this.setValue(_this.getValue());
32909                 }
32910             }
32911         });
32912         
32913         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32914         
32915         this.yearField = new Roo.bootstrap.ComboBox({
32916             allowBlank : this.yearAllowBlank,
32917             alwaysQuery : true,
32918             displayField : 'value',
32919             editable : false,
32920             fieldLabel : '',
32921             forceSelection : true,
32922             mode : 'local',
32923             placeholder : this.yearPlaceholder,
32924             selectOnFocus : true,
32925             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32926             triggerAction : 'all',
32927             typeAhead : true,
32928             valueField : 'value',
32929             store : new Roo.data.SimpleStore({
32930                 data : (function() {
32931                     var years = [];
32932                     _this.fireEvent('years', _this, years);
32933                     return years;
32934                 })(),
32935                 fields : [ 'value' ]
32936             }),
32937             listeners : {
32938                 select : function (_self, record, index)
32939                 {
32940                     _this.setValue(_this.getValue());
32941                 }
32942             }
32943         });
32944
32945         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32946     },
32947     
32948     setValue : function(v, format)
32949     {
32950         this.inputEl.dom.value = v;
32951         
32952         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32953         
32954         var d = Date.parseDate(v, f);
32955         
32956         if(!d){
32957             this.validate();
32958             return;
32959         }
32960         
32961         this.setDay(d.format(this.dayFormat));
32962         this.setMonth(d.format(this.monthFormat));
32963         this.setYear(d.format(this.yearFormat));
32964         
32965         this.validate();
32966         
32967         return;
32968     },
32969     
32970     setDay : function(v)
32971     {
32972         this.dayField.setValue(v);
32973         this.inputEl.dom.value = this.getValue();
32974         this.validate();
32975         return;
32976     },
32977     
32978     setMonth : function(v)
32979     {
32980         this.monthField.setValue(v, true);
32981         this.inputEl.dom.value = this.getValue();
32982         this.validate();
32983         return;
32984     },
32985     
32986     setYear : function(v)
32987     {
32988         this.yearField.setValue(v);
32989         this.inputEl.dom.value = this.getValue();
32990         this.validate();
32991         return;
32992     },
32993     
32994     getDay : function()
32995     {
32996         return this.dayField.getValue();
32997     },
32998     
32999     getMonth : function()
33000     {
33001         return this.monthField.getValue();
33002     },
33003     
33004     getYear : function()
33005     {
33006         return this.yearField.getValue();
33007     },
33008     
33009     getValue : function()
33010     {
33011         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33012         
33013         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33014         
33015         return date;
33016     },
33017     
33018     reset : function()
33019     {
33020         this.setDay('');
33021         this.setMonth('');
33022         this.setYear('');
33023         this.inputEl.dom.value = '';
33024         this.validate();
33025         return;
33026     },
33027     
33028     validate : function()
33029     {
33030         var d = this.dayField.validate();
33031         var m = this.monthField.validate();
33032         var y = this.yearField.validate();
33033         
33034         var valid = true;
33035         
33036         if(
33037                 (!this.dayAllowBlank && !d) ||
33038                 (!this.monthAllowBlank && !m) ||
33039                 (!this.yearAllowBlank && !y)
33040         ){
33041             valid = false;
33042         }
33043         
33044         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33045             return valid;
33046         }
33047         
33048         if(valid){
33049             this.markValid();
33050             return valid;
33051         }
33052         
33053         this.markInvalid();
33054         
33055         return valid;
33056     },
33057     
33058     markValid : function()
33059     {
33060         
33061         var label = this.el.select('label', true).first();
33062         var icon = this.el.select('i.fa-star', true).first();
33063
33064         if(label && icon){
33065             icon.remove();
33066         }
33067         
33068         this.fireEvent('valid', this);
33069     },
33070     
33071      /**
33072      * Mark this field as invalid
33073      * @param {String} msg The validation message
33074      */
33075     markInvalid : function(msg)
33076     {
33077         
33078         var label = this.el.select('label', true).first();
33079         var icon = this.el.select('i.fa-star', true).first();
33080
33081         if(label && !icon){
33082             this.el.select('.roo-date-split-field-label', true).createChild({
33083                 tag : 'i',
33084                 cls : 'text-danger fa fa-lg fa-star',
33085                 tooltip : 'This field is required',
33086                 style : 'margin-right:5px;'
33087             }, label, true);
33088         }
33089         
33090         this.fireEvent('invalid', this, msg);
33091     },
33092     
33093     clearInvalid : function()
33094     {
33095         var label = this.el.select('label', true).first();
33096         var icon = this.el.select('i.fa-star', true).first();
33097
33098         if(label && icon){
33099             icon.remove();
33100         }
33101         
33102         this.fireEvent('valid', this);
33103     },
33104     
33105     getName: function()
33106     {
33107         return this.name;
33108     }
33109     
33110 });
33111
33112  /**
33113  *
33114  * This is based on 
33115  * http://masonry.desandro.com
33116  *
33117  * The idea is to render all the bricks based on vertical width...
33118  *
33119  * The original code extends 'outlayer' - we might need to use that....
33120  * 
33121  */
33122
33123
33124 /**
33125  * @class Roo.bootstrap.LayoutMasonry
33126  * @extends Roo.bootstrap.Component
33127  * Bootstrap Layout Masonry class
33128  * 
33129  * @constructor
33130  * Create a new Element
33131  * @param {Object} config The config object
33132  */
33133
33134 Roo.bootstrap.LayoutMasonry = function(config){
33135     
33136     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33137     
33138     this.bricks = [];
33139     
33140     Roo.bootstrap.LayoutMasonry.register(this);
33141     
33142     this.addEvents({
33143         // raw events
33144         /**
33145          * @event layout
33146          * Fire after layout the items
33147          * @param {Roo.bootstrap.LayoutMasonry} this
33148          * @param {Roo.EventObject} e
33149          */
33150         "layout" : true
33151     });
33152     
33153 };
33154
33155 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33156     
33157     /**
33158      * @cfg {Boolean} isLayoutInstant = no animation?
33159      */   
33160     isLayoutInstant : false, // needed?
33161    
33162     /**
33163      * @cfg {Number} boxWidth  width of the columns
33164      */   
33165     boxWidth : 450,
33166     
33167       /**
33168      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33169      */   
33170     boxHeight : 0,
33171     
33172     /**
33173      * @cfg {Number} padWidth padding below box..
33174      */   
33175     padWidth : 10, 
33176     
33177     /**
33178      * @cfg {Number} gutter gutter width..
33179      */   
33180     gutter : 10,
33181     
33182      /**
33183      * @cfg {Number} maxCols maximum number of columns
33184      */   
33185     
33186     maxCols: 0,
33187     
33188     /**
33189      * @cfg {Boolean} isAutoInitial defalut true
33190      */   
33191     isAutoInitial : true, 
33192     
33193     containerWidth: 0,
33194     
33195     /**
33196      * @cfg {Boolean} isHorizontal defalut false
33197      */   
33198     isHorizontal : false, 
33199
33200     currentSize : null,
33201     
33202     tag: 'div',
33203     
33204     cls: '',
33205     
33206     bricks: null, //CompositeElement
33207     
33208     cols : 1,
33209     
33210     _isLayoutInited : false,
33211     
33212 //    isAlternative : false, // only use for vertical layout...
33213     
33214     /**
33215      * @cfg {Number} alternativePadWidth padding below box..
33216      */   
33217     alternativePadWidth : 50,
33218     
33219     selectedBrick : [],
33220     
33221     getAutoCreate : function(){
33222         
33223         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33224         
33225         var cfg = {
33226             tag: this.tag,
33227             cls: 'blog-masonary-wrapper ' + this.cls,
33228             cn : {
33229                 cls : 'mas-boxes masonary'
33230             }
33231         };
33232         
33233         return cfg;
33234     },
33235     
33236     getChildContainer: function( )
33237     {
33238         if (this.boxesEl) {
33239             return this.boxesEl;
33240         }
33241         
33242         this.boxesEl = this.el.select('.mas-boxes').first();
33243         
33244         return this.boxesEl;
33245     },
33246     
33247     
33248     initEvents : function()
33249     {
33250         var _this = this;
33251         
33252         if(this.isAutoInitial){
33253             Roo.log('hook children rendered');
33254             this.on('childrenrendered', function() {
33255                 Roo.log('children rendered');
33256                 _this.initial();
33257             } ,this);
33258         }
33259     },
33260     
33261     initial : function()
33262     {
33263         this.selectedBrick = [];
33264         
33265         this.currentSize = this.el.getBox(true);
33266         
33267         Roo.EventManager.onWindowResize(this.resize, this); 
33268
33269         if(!this.isAutoInitial){
33270             this.layout();
33271             return;
33272         }
33273         
33274         this.layout();
33275         
33276         return;
33277         //this.layout.defer(500,this);
33278         
33279     },
33280     
33281     resize : function()
33282     {
33283         var cs = this.el.getBox(true);
33284         
33285         if (
33286                 this.currentSize.width == cs.width && 
33287                 this.currentSize.x == cs.x && 
33288                 this.currentSize.height == cs.height && 
33289                 this.currentSize.y == cs.y 
33290         ) {
33291             Roo.log("no change in with or X or Y");
33292             return;
33293         }
33294         
33295         this.currentSize = cs;
33296         
33297         this.layout();
33298         
33299     },
33300     
33301     layout : function()
33302     {   
33303         this._resetLayout();
33304         
33305         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33306         
33307         this.layoutItems( isInstant );
33308       
33309         this._isLayoutInited = true;
33310         
33311         this.fireEvent('layout', this);
33312         
33313     },
33314     
33315     _resetLayout : function()
33316     {
33317         if(this.isHorizontal){
33318             this.horizontalMeasureColumns();
33319             return;
33320         }
33321         
33322         this.verticalMeasureColumns();
33323         
33324     },
33325     
33326     verticalMeasureColumns : function()
33327     {
33328         this.getContainerWidth();
33329         
33330 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33331 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33332 //            return;
33333 //        }
33334         
33335         var boxWidth = this.boxWidth + this.padWidth;
33336         
33337         if(this.containerWidth < this.boxWidth){
33338             boxWidth = this.containerWidth
33339         }
33340         
33341         var containerWidth = this.containerWidth;
33342         
33343         var cols = Math.floor(containerWidth / boxWidth);
33344         
33345         this.cols = Math.max( cols, 1 );
33346         
33347         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33348         
33349         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33350         
33351         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33352         
33353         this.colWidth = boxWidth + avail - this.padWidth;
33354         
33355         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33356         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33357     },
33358     
33359     horizontalMeasureColumns : function()
33360     {
33361         this.getContainerWidth();
33362         
33363         var boxWidth = this.boxWidth;
33364         
33365         if(this.containerWidth < boxWidth){
33366             boxWidth = this.containerWidth;
33367         }
33368         
33369         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33370         
33371         this.el.setHeight(boxWidth);
33372         
33373     },
33374     
33375     getContainerWidth : function()
33376     {
33377         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33378     },
33379     
33380     layoutItems : function( isInstant )
33381     {
33382         Roo.log(this.bricks);
33383         
33384         var items = Roo.apply([], this.bricks);
33385         
33386         if(this.isHorizontal){
33387             this._horizontalLayoutItems( items , isInstant );
33388             return;
33389         }
33390         
33391 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33392 //            this._verticalAlternativeLayoutItems( items , isInstant );
33393 //            return;
33394 //        }
33395         
33396         this._verticalLayoutItems( items , isInstant );
33397         
33398     },
33399     
33400     _verticalLayoutItems : function ( items , isInstant)
33401     {
33402         if ( !items || !items.length ) {
33403             return;
33404         }
33405         
33406         var standard = [
33407             ['xs', 'xs', 'xs', 'tall'],
33408             ['xs', 'xs', 'tall'],
33409             ['xs', 'xs', 'sm'],
33410             ['xs', 'xs', 'xs'],
33411             ['xs', 'tall'],
33412             ['xs', 'sm'],
33413             ['xs', 'xs'],
33414             ['xs'],
33415             
33416             ['sm', 'xs', 'xs'],
33417             ['sm', 'xs'],
33418             ['sm'],
33419             
33420             ['tall', 'xs', 'xs', 'xs'],
33421             ['tall', 'xs', 'xs'],
33422             ['tall', 'xs'],
33423             ['tall']
33424             
33425         ];
33426         
33427         var queue = [];
33428         
33429         var boxes = [];
33430         
33431         var box = [];
33432         
33433         Roo.each(items, function(item, k){
33434             
33435             switch (item.size) {
33436                 // these layouts take up a full box,
33437                 case 'md' :
33438                 case 'md-left' :
33439                 case 'md-right' :
33440                 case 'wide' :
33441                     
33442                     if(box.length){
33443                         boxes.push(box);
33444                         box = [];
33445                     }
33446                     
33447                     boxes.push([item]);
33448                     
33449                     break;
33450                     
33451                 case 'xs' :
33452                 case 'sm' :
33453                 case 'tall' :
33454                     
33455                     box.push(item);
33456                     
33457                     break;
33458                 default :
33459                     break;
33460                     
33461             }
33462             
33463         }, this);
33464         
33465         if(box.length){
33466             boxes.push(box);
33467             box = [];
33468         }
33469         
33470         var filterPattern = function(box, length)
33471         {
33472             if(!box.length){
33473                 return;
33474             }
33475             
33476             var match = false;
33477             
33478             var pattern = box.slice(0, length);
33479             
33480             var format = [];
33481             
33482             Roo.each(pattern, function(i){
33483                 format.push(i.size);
33484             }, this);
33485             
33486             Roo.each(standard, function(s){
33487                 
33488                 if(String(s) != String(format)){
33489                     return;
33490                 }
33491                 
33492                 match = true;
33493                 return false;
33494                 
33495             }, this);
33496             
33497             if(!match && length == 1){
33498                 return;
33499             }
33500             
33501             if(!match){
33502                 filterPattern(box, length - 1);
33503                 return;
33504             }
33505                 
33506             queue.push(pattern);
33507
33508             box = box.slice(length, box.length);
33509
33510             filterPattern(box, 4);
33511
33512             return;
33513             
33514         }
33515         
33516         Roo.each(boxes, function(box, k){
33517             
33518             if(!box.length){
33519                 return;
33520             }
33521             
33522             if(box.length == 1){
33523                 queue.push(box);
33524                 return;
33525             }
33526             
33527             filterPattern(box, 4);
33528             
33529         }, this);
33530         
33531         this._processVerticalLayoutQueue( queue, isInstant );
33532         
33533     },
33534     
33535 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33536 //    {
33537 //        if ( !items || !items.length ) {
33538 //            return;
33539 //        }
33540 //
33541 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33542 //        
33543 //    },
33544     
33545     _horizontalLayoutItems : function ( items , isInstant)
33546     {
33547         if ( !items || !items.length || items.length < 3) {
33548             return;
33549         }
33550         
33551         items.reverse();
33552         
33553         var eItems = items.slice(0, 3);
33554         
33555         items = items.slice(3, items.length);
33556         
33557         var standard = [
33558             ['xs', 'xs', 'xs', 'wide'],
33559             ['xs', 'xs', 'wide'],
33560             ['xs', 'xs', 'sm'],
33561             ['xs', 'xs', 'xs'],
33562             ['xs', 'wide'],
33563             ['xs', 'sm'],
33564             ['xs', 'xs'],
33565             ['xs'],
33566             
33567             ['sm', 'xs', 'xs'],
33568             ['sm', 'xs'],
33569             ['sm'],
33570             
33571             ['wide', 'xs', 'xs', 'xs'],
33572             ['wide', 'xs', 'xs'],
33573             ['wide', 'xs'],
33574             ['wide'],
33575             
33576             ['wide-thin']
33577         ];
33578         
33579         var queue = [];
33580         
33581         var boxes = [];
33582         
33583         var box = [];
33584         
33585         Roo.each(items, function(item, k){
33586             
33587             switch (item.size) {
33588                 case 'md' :
33589                 case 'md-left' :
33590                 case 'md-right' :
33591                 case 'tall' :
33592                     
33593                     if(box.length){
33594                         boxes.push(box);
33595                         box = [];
33596                     }
33597                     
33598                     boxes.push([item]);
33599                     
33600                     break;
33601                     
33602                 case 'xs' :
33603                 case 'sm' :
33604                 case 'wide' :
33605                 case 'wide-thin' :
33606                     
33607                     box.push(item);
33608                     
33609                     break;
33610                 default :
33611                     break;
33612                     
33613             }
33614             
33615         }, this);
33616         
33617         if(box.length){
33618             boxes.push(box);
33619             box = [];
33620         }
33621         
33622         var filterPattern = function(box, length)
33623         {
33624             if(!box.length){
33625                 return;
33626             }
33627             
33628             var match = false;
33629             
33630             var pattern = box.slice(0, length);
33631             
33632             var format = [];
33633             
33634             Roo.each(pattern, function(i){
33635                 format.push(i.size);
33636             }, this);
33637             
33638             Roo.each(standard, function(s){
33639                 
33640                 if(String(s) != String(format)){
33641                     return;
33642                 }
33643                 
33644                 match = true;
33645                 return false;
33646                 
33647             }, this);
33648             
33649             if(!match && length == 1){
33650                 return;
33651             }
33652             
33653             if(!match){
33654                 filterPattern(box, length - 1);
33655                 return;
33656             }
33657                 
33658             queue.push(pattern);
33659
33660             box = box.slice(length, box.length);
33661
33662             filterPattern(box, 4);
33663
33664             return;
33665             
33666         }
33667         
33668         Roo.each(boxes, function(box, k){
33669             
33670             if(!box.length){
33671                 return;
33672             }
33673             
33674             if(box.length == 1){
33675                 queue.push(box);
33676                 return;
33677             }
33678             
33679             filterPattern(box, 4);
33680             
33681         }, this);
33682         
33683         
33684         var prune = [];
33685         
33686         var pos = this.el.getBox(true);
33687         
33688         var minX = pos.x;
33689         
33690         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33691         
33692         var hit_end = false;
33693         
33694         Roo.each(queue, function(box){
33695             
33696             if(hit_end){
33697                 
33698                 Roo.each(box, function(b){
33699                 
33700                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33701                     b.el.hide();
33702
33703                 }, this);
33704
33705                 return;
33706             }
33707             
33708             var mx = 0;
33709             
33710             Roo.each(box, function(b){
33711                 
33712                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33713                 b.el.show();
33714
33715                 mx = Math.max(mx, b.x);
33716                 
33717             }, this);
33718             
33719             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33720             
33721             if(maxX < minX){
33722                 
33723                 Roo.each(box, function(b){
33724                 
33725                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33726                     b.el.hide();
33727                     
33728                 }, this);
33729                 
33730                 hit_end = true;
33731                 
33732                 return;
33733             }
33734             
33735             prune.push(box);
33736             
33737         }, this);
33738         
33739         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33740     },
33741     
33742     /** Sets position of item in DOM
33743     * @param {Element} item
33744     * @param {Number} x - horizontal position
33745     * @param {Number} y - vertical position
33746     * @param {Boolean} isInstant - disables transitions
33747     */
33748     _processVerticalLayoutQueue : function( queue, isInstant )
33749     {
33750         var pos = this.el.getBox(true);
33751         var x = pos.x;
33752         var y = pos.y;
33753         var maxY = [];
33754         
33755         for (var i = 0; i < this.cols; i++){
33756             maxY[i] = pos.y;
33757         }
33758         
33759         Roo.each(queue, function(box, k){
33760             
33761             var col = k % this.cols;
33762             
33763             Roo.each(box, function(b,kk){
33764                 
33765                 b.el.position('absolute');
33766                 
33767                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33768                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33769                 
33770                 if(b.size == 'md-left' || b.size == 'md-right'){
33771                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33772                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33773                 }
33774                 
33775                 b.el.setWidth(width);
33776                 b.el.setHeight(height);
33777                 // iframe?
33778                 b.el.select('iframe',true).setSize(width,height);
33779                 
33780             }, this);
33781             
33782             for (var i = 0; i < this.cols; i++){
33783                 
33784                 if(maxY[i] < maxY[col]){
33785                     col = i;
33786                     continue;
33787                 }
33788                 
33789                 col = Math.min(col, i);
33790                 
33791             }
33792             
33793             x = pos.x + col * (this.colWidth + this.padWidth);
33794             
33795             y = maxY[col];
33796             
33797             var positions = [];
33798             
33799             switch (box.length){
33800                 case 1 :
33801                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33802                     break;
33803                 case 2 :
33804                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33805                     break;
33806                 case 3 :
33807                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33808                     break;
33809                 case 4 :
33810                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33811                     break;
33812                 default :
33813                     break;
33814             }
33815             
33816             Roo.each(box, function(b,kk){
33817                 
33818                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33819                 
33820                 var sz = b.el.getSize();
33821                 
33822                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33823                 
33824             }, this);
33825             
33826         }, this);
33827         
33828         var mY = 0;
33829         
33830         for (var i = 0; i < this.cols; i++){
33831             mY = Math.max(mY, maxY[i]);
33832         }
33833         
33834         this.el.setHeight(mY - pos.y);
33835         
33836     },
33837     
33838 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33839 //    {
33840 //        var pos = this.el.getBox(true);
33841 //        var x = pos.x;
33842 //        var y = pos.y;
33843 //        var maxX = pos.right;
33844 //        
33845 //        var maxHeight = 0;
33846 //        
33847 //        Roo.each(items, function(item, k){
33848 //            
33849 //            var c = k % 2;
33850 //            
33851 //            item.el.position('absolute');
33852 //                
33853 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33854 //
33855 //            item.el.setWidth(width);
33856 //
33857 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33858 //
33859 //            item.el.setHeight(height);
33860 //            
33861 //            if(c == 0){
33862 //                item.el.setXY([x, y], isInstant ? false : true);
33863 //            } else {
33864 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33865 //            }
33866 //            
33867 //            y = y + height + this.alternativePadWidth;
33868 //            
33869 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33870 //            
33871 //        }, this);
33872 //        
33873 //        this.el.setHeight(maxHeight);
33874 //        
33875 //    },
33876     
33877     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33878     {
33879         var pos = this.el.getBox(true);
33880         
33881         var minX = pos.x;
33882         var minY = pos.y;
33883         
33884         var maxX = pos.right;
33885         
33886         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33887         
33888         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33889         
33890         Roo.each(queue, function(box, k){
33891             
33892             Roo.each(box, function(b, kk){
33893                 
33894                 b.el.position('absolute');
33895                 
33896                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33897                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33898                 
33899                 if(b.size == 'md-left' || b.size == 'md-right'){
33900                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33901                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33902                 }
33903                 
33904                 b.el.setWidth(width);
33905                 b.el.setHeight(height);
33906                 
33907             }, this);
33908             
33909             if(!box.length){
33910                 return;
33911             }
33912             
33913             var positions = [];
33914             
33915             switch (box.length){
33916                 case 1 :
33917                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33918                     break;
33919                 case 2 :
33920                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33921                     break;
33922                 case 3 :
33923                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33924                     break;
33925                 case 4 :
33926                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33927                     break;
33928                 default :
33929                     break;
33930             }
33931             
33932             Roo.each(box, function(b,kk){
33933                 
33934                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33935                 
33936                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33937                 
33938             }, this);
33939             
33940         }, this);
33941         
33942     },
33943     
33944     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33945     {
33946         Roo.each(eItems, function(b,k){
33947             
33948             b.size = (k == 0) ? 'sm' : 'xs';
33949             b.x = (k == 0) ? 2 : 1;
33950             b.y = (k == 0) ? 2 : 1;
33951             
33952             b.el.position('absolute');
33953             
33954             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33955                 
33956             b.el.setWidth(width);
33957             
33958             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33959             
33960             b.el.setHeight(height);
33961             
33962         }, this);
33963
33964         var positions = [];
33965         
33966         positions.push({
33967             x : maxX - this.unitWidth * 2 - this.gutter,
33968             y : minY
33969         });
33970         
33971         positions.push({
33972             x : maxX - this.unitWidth,
33973             y : minY + (this.unitWidth + this.gutter) * 2
33974         });
33975         
33976         positions.push({
33977             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33978             y : minY
33979         });
33980         
33981         Roo.each(eItems, function(b,k){
33982             
33983             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33984
33985         }, this);
33986         
33987     },
33988     
33989     getVerticalOneBoxColPositions : function(x, y, box)
33990     {
33991         var pos = [];
33992         
33993         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33994         
33995         if(box[0].size == 'md-left'){
33996             rand = 0;
33997         }
33998         
33999         if(box[0].size == 'md-right'){
34000             rand = 1;
34001         }
34002         
34003         pos.push({
34004             x : x + (this.unitWidth + this.gutter) * rand,
34005             y : y
34006         });
34007         
34008         return pos;
34009     },
34010     
34011     getVerticalTwoBoxColPositions : function(x, y, box)
34012     {
34013         var pos = [];
34014         
34015         if(box[0].size == 'xs'){
34016             
34017             pos.push({
34018                 x : x,
34019                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34020             });
34021
34022             pos.push({
34023                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34024                 y : y
34025             });
34026             
34027             return pos;
34028             
34029         }
34030         
34031         pos.push({
34032             x : x,
34033             y : y
34034         });
34035
34036         pos.push({
34037             x : x + (this.unitWidth + this.gutter) * 2,
34038             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34039         });
34040         
34041         return pos;
34042         
34043     },
34044     
34045     getVerticalThreeBoxColPositions : function(x, y, box)
34046     {
34047         var pos = [];
34048         
34049         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34050             
34051             pos.push({
34052                 x : x,
34053                 y : y
34054             });
34055
34056             pos.push({
34057                 x : x + (this.unitWidth + this.gutter) * 1,
34058                 y : y
34059             });
34060             
34061             pos.push({
34062                 x : x + (this.unitWidth + this.gutter) * 2,
34063                 y : y
34064             });
34065             
34066             return pos;
34067             
34068         }
34069         
34070         if(box[0].size == 'xs' && box[1].size == 'xs'){
34071             
34072             pos.push({
34073                 x : x,
34074                 y : y
34075             });
34076
34077             pos.push({
34078                 x : x,
34079                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34080             });
34081             
34082             pos.push({
34083                 x : x + (this.unitWidth + this.gutter) * 1,
34084                 y : y
34085             });
34086             
34087             return pos;
34088             
34089         }
34090         
34091         pos.push({
34092             x : x,
34093             y : y
34094         });
34095
34096         pos.push({
34097             x : x + (this.unitWidth + this.gutter) * 2,
34098             y : y
34099         });
34100
34101         pos.push({
34102             x : x + (this.unitWidth + this.gutter) * 2,
34103             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34104         });
34105             
34106         return pos;
34107         
34108     },
34109     
34110     getVerticalFourBoxColPositions : function(x, y, box)
34111     {
34112         var pos = [];
34113         
34114         if(box[0].size == 'xs'){
34115             
34116             pos.push({
34117                 x : x,
34118                 y : y
34119             });
34120
34121             pos.push({
34122                 x : x,
34123                 y : y + (this.unitHeight + this.gutter) * 1
34124             });
34125             
34126             pos.push({
34127                 x : x,
34128                 y : y + (this.unitHeight + this.gutter) * 2
34129             });
34130             
34131             pos.push({
34132                 x : x + (this.unitWidth + this.gutter) * 1,
34133                 y : y
34134             });
34135             
34136             return pos;
34137             
34138         }
34139         
34140         pos.push({
34141             x : x,
34142             y : y
34143         });
34144
34145         pos.push({
34146             x : x + (this.unitWidth + this.gutter) * 2,
34147             y : y
34148         });
34149
34150         pos.push({
34151             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34152             y : y + (this.unitHeight + this.gutter) * 1
34153         });
34154
34155         pos.push({
34156             x : x + (this.unitWidth + this.gutter) * 2,
34157             y : y + (this.unitWidth + this.gutter) * 2
34158         });
34159
34160         return pos;
34161         
34162     },
34163     
34164     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34165     {
34166         var pos = [];
34167         
34168         if(box[0].size == 'md-left'){
34169             pos.push({
34170                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34171                 y : minY
34172             });
34173             
34174             return pos;
34175         }
34176         
34177         if(box[0].size == 'md-right'){
34178             pos.push({
34179                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34180                 y : minY + (this.unitWidth + this.gutter) * 1
34181             });
34182             
34183             return pos;
34184         }
34185         
34186         var rand = Math.floor(Math.random() * (4 - box[0].y));
34187         
34188         pos.push({
34189             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34190             y : minY + (this.unitWidth + this.gutter) * rand
34191         });
34192         
34193         return pos;
34194         
34195     },
34196     
34197     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34198     {
34199         var pos = [];
34200         
34201         if(box[0].size == 'xs'){
34202             
34203             pos.push({
34204                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34205                 y : minY
34206             });
34207
34208             pos.push({
34209                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34210                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34211             });
34212             
34213             return pos;
34214             
34215         }
34216         
34217         pos.push({
34218             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34219             y : minY
34220         });
34221
34222         pos.push({
34223             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34224             y : minY + (this.unitWidth + this.gutter) * 2
34225         });
34226         
34227         return pos;
34228         
34229     },
34230     
34231     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34232     {
34233         var pos = [];
34234         
34235         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34236             
34237             pos.push({
34238                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34239                 y : minY
34240             });
34241
34242             pos.push({
34243                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34244                 y : minY + (this.unitWidth + this.gutter) * 1
34245             });
34246             
34247             pos.push({
34248                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34249                 y : minY + (this.unitWidth + this.gutter) * 2
34250             });
34251             
34252             return pos;
34253             
34254         }
34255         
34256         if(box[0].size == 'xs' && box[1].size == 'xs'){
34257             
34258             pos.push({
34259                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34260                 y : minY
34261             });
34262
34263             pos.push({
34264                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34265                 y : minY
34266             });
34267             
34268             pos.push({
34269                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34270                 y : minY + (this.unitWidth + this.gutter) * 1
34271             });
34272             
34273             return pos;
34274             
34275         }
34276         
34277         pos.push({
34278             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34279             y : minY
34280         });
34281
34282         pos.push({
34283             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34284             y : minY + (this.unitWidth + this.gutter) * 2
34285         });
34286
34287         pos.push({
34288             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34289             y : minY + (this.unitWidth + this.gutter) * 2
34290         });
34291             
34292         return pos;
34293         
34294     },
34295     
34296     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34297     {
34298         var pos = [];
34299         
34300         if(box[0].size == 'xs'){
34301             
34302             pos.push({
34303                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34304                 y : minY
34305             });
34306
34307             pos.push({
34308                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34309                 y : minY
34310             });
34311             
34312             pos.push({
34313                 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),
34314                 y : minY
34315             });
34316             
34317             pos.push({
34318                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34319                 y : minY + (this.unitWidth + this.gutter) * 1
34320             });
34321             
34322             return pos;
34323             
34324         }
34325         
34326         pos.push({
34327             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34328             y : minY
34329         });
34330         
34331         pos.push({
34332             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34333             y : minY + (this.unitWidth + this.gutter) * 2
34334         });
34335         
34336         pos.push({
34337             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34338             y : minY + (this.unitWidth + this.gutter) * 2
34339         });
34340         
34341         pos.push({
34342             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),
34343             y : minY + (this.unitWidth + this.gutter) * 2
34344         });
34345
34346         return pos;
34347         
34348     },
34349     
34350     /**
34351     * remove a Masonry Brick
34352     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34353     */
34354     removeBrick : function(brick_id)
34355     {
34356         if (!brick_id) {
34357             return;
34358         }
34359         
34360         for (var i = 0; i<this.bricks.length; i++) {
34361             if (this.bricks[i].id == brick_id) {
34362                 this.bricks.splice(i,1);
34363                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34364                 this.initial();
34365             }
34366         }
34367     },
34368     
34369     /**
34370     * adds a Masonry Brick
34371     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34372     */
34373     addBrick : function(cfg)
34374     {
34375         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34376         //this.register(cn);
34377         cn.parentId = this.id;
34378         cn.render(this.el);
34379         return cn;
34380     },
34381     
34382     /**
34383     * register a Masonry Brick
34384     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34385     */
34386     
34387     register : function(brick)
34388     {
34389         this.bricks.push(brick);
34390         brick.masonryId = this.id;
34391     },
34392     
34393     /**
34394     * clear all the Masonry Brick
34395     */
34396     clearAll : function()
34397     {
34398         this.bricks = [];
34399         //this.getChildContainer().dom.innerHTML = "";
34400         this.el.dom.innerHTML = '';
34401     },
34402     
34403     getSelected : function()
34404     {
34405         if (!this.selectedBrick) {
34406             return false;
34407         }
34408         
34409         return this.selectedBrick;
34410     }
34411 });
34412
34413 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34414     
34415     groups: {},
34416      /**
34417     * register a Masonry Layout
34418     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34419     */
34420     
34421     register : function(layout)
34422     {
34423         this.groups[layout.id] = layout;
34424     },
34425     /**
34426     * fetch a  Masonry Layout based on the masonry layout ID
34427     * @param {string} the masonry layout to add
34428     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34429     */
34430     
34431     get: function(layout_id) {
34432         if (typeof(this.groups[layout_id]) == 'undefined') {
34433             return false;
34434         }
34435         return this.groups[layout_id] ;
34436     }
34437     
34438     
34439     
34440 });
34441
34442  
34443
34444  /**
34445  *
34446  * This is based on 
34447  * http://masonry.desandro.com
34448  *
34449  * The idea is to render all the bricks based on vertical width...
34450  *
34451  * The original code extends 'outlayer' - we might need to use that....
34452  * 
34453  */
34454
34455
34456 /**
34457  * @class Roo.bootstrap.LayoutMasonryAuto
34458  * @extends Roo.bootstrap.Component
34459  * Bootstrap Layout Masonry class
34460  * 
34461  * @constructor
34462  * Create a new Element
34463  * @param {Object} config The config object
34464  */
34465
34466 Roo.bootstrap.LayoutMasonryAuto = function(config){
34467     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34468 };
34469
34470 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34471     
34472       /**
34473      * @cfg {Boolean} isFitWidth  - resize the width..
34474      */   
34475     isFitWidth : false,  // options..
34476     /**
34477      * @cfg {Boolean} isOriginLeft = left align?
34478      */   
34479     isOriginLeft : true,
34480     /**
34481      * @cfg {Boolean} isOriginTop = top align?
34482      */   
34483     isOriginTop : false,
34484     /**
34485      * @cfg {Boolean} isLayoutInstant = no animation?
34486      */   
34487     isLayoutInstant : false, // needed?
34488     /**
34489      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34490      */   
34491     isResizingContainer : true,
34492     /**
34493      * @cfg {Number} columnWidth  width of the columns 
34494      */   
34495     
34496     columnWidth : 0,
34497     
34498     /**
34499      * @cfg {Number} maxCols maximum number of columns
34500      */   
34501     
34502     maxCols: 0,
34503     /**
34504      * @cfg {Number} padHeight padding below box..
34505      */   
34506     
34507     padHeight : 10, 
34508     
34509     /**
34510      * @cfg {Boolean} isAutoInitial defalut true
34511      */   
34512     
34513     isAutoInitial : true, 
34514     
34515     // private?
34516     gutter : 0,
34517     
34518     containerWidth: 0,
34519     initialColumnWidth : 0,
34520     currentSize : null,
34521     
34522     colYs : null, // array.
34523     maxY : 0,
34524     padWidth: 10,
34525     
34526     
34527     tag: 'div',
34528     cls: '',
34529     bricks: null, //CompositeElement
34530     cols : 0, // array?
34531     // element : null, // wrapped now this.el
34532     _isLayoutInited : null, 
34533     
34534     
34535     getAutoCreate : function(){
34536         
34537         var cfg = {
34538             tag: this.tag,
34539             cls: 'blog-masonary-wrapper ' + this.cls,
34540             cn : {
34541                 cls : 'mas-boxes masonary'
34542             }
34543         };
34544         
34545         return cfg;
34546     },
34547     
34548     getChildContainer: function( )
34549     {
34550         if (this.boxesEl) {
34551             return this.boxesEl;
34552         }
34553         
34554         this.boxesEl = this.el.select('.mas-boxes').first();
34555         
34556         return this.boxesEl;
34557     },
34558     
34559     
34560     initEvents : function()
34561     {
34562         var _this = this;
34563         
34564         if(this.isAutoInitial){
34565             Roo.log('hook children rendered');
34566             this.on('childrenrendered', function() {
34567                 Roo.log('children rendered');
34568                 _this.initial();
34569             } ,this);
34570         }
34571         
34572     },
34573     
34574     initial : function()
34575     {
34576         this.reloadItems();
34577
34578         this.currentSize = this.el.getBox(true);
34579
34580         /// was window resize... - let's see if this works..
34581         Roo.EventManager.onWindowResize(this.resize, this); 
34582
34583         if(!this.isAutoInitial){
34584             this.layout();
34585             return;
34586         }
34587         
34588         this.layout.defer(500,this);
34589     },
34590     
34591     reloadItems: function()
34592     {
34593         this.bricks = this.el.select('.masonry-brick', true);
34594         
34595         this.bricks.each(function(b) {
34596             //Roo.log(b.getSize());
34597             if (!b.attr('originalwidth')) {
34598                 b.attr('originalwidth',  b.getSize().width);
34599             }
34600             
34601         });
34602         
34603         Roo.log(this.bricks.elements.length);
34604     },
34605     
34606     resize : function()
34607     {
34608         Roo.log('resize');
34609         var cs = this.el.getBox(true);
34610         
34611         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34612             Roo.log("no change in with or X");
34613             return;
34614         }
34615         this.currentSize = cs;
34616         this.layout();
34617     },
34618     
34619     layout : function()
34620     {
34621          Roo.log('layout');
34622         this._resetLayout();
34623         //this._manageStamps();
34624       
34625         // don't animate first layout
34626         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34627         this.layoutItems( isInstant );
34628       
34629         // flag for initalized
34630         this._isLayoutInited = true;
34631     },
34632     
34633     layoutItems : function( isInstant )
34634     {
34635         //var items = this._getItemsForLayout( this.items );
34636         // original code supports filtering layout items.. we just ignore it..
34637         
34638         this._layoutItems( this.bricks , isInstant );
34639       
34640         this._postLayout();
34641     },
34642     _layoutItems : function ( items , isInstant)
34643     {
34644        //this.fireEvent( 'layout', this, items );
34645     
34646
34647         if ( !items || !items.elements.length ) {
34648           // no items, emit event with empty array
34649             return;
34650         }
34651
34652         var queue = [];
34653         items.each(function(item) {
34654             Roo.log("layout item");
34655             Roo.log(item);
34656             // get x/y object from method
34657             var position = this._getItemLayoutPosition( item );
34658             // enqueue
34659             position.item = item;
34660             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34661             queue.push( position );
34662         }, this);
34663       
34664         this._processLayoutQueue( queue );
34665     },
34666     /** Sets position of item in DOM
34667     * @param {Element} item
34668     * @param {Number} x - horizontal position
34669     * @param {Number} y - vertical position
34670     * @param {Boolean} isInstant - disables transitions
34671     */
34672     _processLayoutQueue : function( queue )
34673     {
34674         for ( var i=0, len = queue.length; i < len; i++ ) {
34675             var obj = queue[i];
34676             obj.item.position('absolute');
34677             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34678         }
34679     },
34680       
34681     
34682     /**
34683     * Any logic you want to do after each layout,
34684     * i.e. size the container
34685     */
34686     _postLayout : function()
34687     {
34688         this.resizeContainer();
34689     },
34690     
34691     resizeContainer : function()
34692     {
34693         if ( !this.isResizingContainer ) {
34694             return;
34695         }
34696         var size = this._getContainerSize();
34697         if ( size ) {
34698             this.el.setSize(size.width,size.height);
34699             this.boxesEl.setSize(size.width,size.height);
34700         }
34701     },
34702     
34703     
34704     
34705     _resetLayout : function()
34706     {
34707         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34708         this.colWidth = this.el.getWidth();
34709         //this.gutter = this.el.getWidth(); 
34710         
34711         this.measureColumns();
34712
34713         // reset column Y
34714         var i = this.cols;
34715         this.colYs = [];
34716         while (i--) {
34717             this.colYs.push( 0 );
34718         }
34719     
34720         this.maxY = 0;
34721     },
34722
34723     measureColumns : function()
34724     {
34725         this.getContainerWidth();
34726       // if columnWidth is 0, default to outerWidth of first item
34727         if ( !this.columnWidth ) {
34728             var firstItem = this.bricks.first();
34729             Roo.log(firstItem);
34730             this.columnWidth  = this.containerWidth;
34731             if (firstItem && firstItem.attr('originalwidth') ) {
34732                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34733             }
34734             // columnWidth fall back to item of first element
34735             Roo.log("set column width?");
34736                         this.initialColumnWidth = this.columnWidth  ;
34737
34738             // if first elem has no width, default to size of container
34739             
34740         }
34741         
34742         
34743         if (this.initialColumnWidth) {
34744             this.columnWidth = this.initialColumnWidth;
34745         }
34746         
34747         
34748             
34749         // column width is fixed at the top - however if container width get's smaller we should
34750         // reduce it...
34751         
34752         // this bit calcs how man columns..
34753             
34754         var columnWidth = this.columnWidth += this.gutter;
34755       
34756         // calculate columns
34757         var containerWidth = this.containerWidth + this.gutter;
34758         
34759         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34760         // fix rounding errors, typically with gutters
34761         var excess = columnWidth - containerWidth % columnWidth;
34762         
34763         
34764         // if overshoot is less than a pixel, round up, otherwise floor it
34765         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34766         cols = Math[ mathMethod ]( cols );
34767         this.cols = Math.max( cols, 1 );
34768         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34769         
34770          // padding positioning..
34771         var totalColWidth = this.cols * this.columnWidth;
34772         var padavail = this.containerWidth - totalColWidth;
34773         // so for 2 columns - we need 3 'pads'
34774         
34775         var padNeeded = (1+this.cols) * this.padWidth;
34776         
34777         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34778         
34779         this.columnWidth += padExtra
34780         //this.padWidth = Math.floor(padavail /  ( this.cols));
34781         
34782         // adjust colum width so that padding is fixed??
34783         
34784         // we have 3 columns ... total = width * 3
34785         // we have X left over... that should be used by 
34786         
34787         //if (this.expandC) {
34788             
34789         //}
34790         
34791         
34792         
34793     },
34794     
34795     getContainerWidth : function()
34796     {
34797        /* // container is parent if fit width
34798         var container = this.isFitWidth ? this.element.parentNode : this.element;
34799         // check that this.size and size are there
34800         // IE8 triggers resize on body size change, so they might not be
34801         
34802         var size = getSize( container );  //FIXME
34803         this.containerWidth = size && size.innerWidth; //FIXME
34804         */
34805          
34806         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34807         
34808     },
34809     
34810     _getItemLayoutPosition : function( item )  // what is item?
34811     {
34812         // we resize the item to our columnWidth..
34813       
34814         item.setWidth(this.columnWidth);
34815         item.autoBoxAdjust  = false;
34816         
34817         var sz = item.getSize();
34818  
34819         // how many columns does this brick span
34820         var remainder = this.containerWidth % this.columnWidth;
34821         
34822         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34823         // round if off by 1 pixel, otherwise use ceil
34824         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34825         colSpan = Math.min( colSpan, this.cols );
34826         
34827         // normally this should be '1' as we dont' currently allow multi width columns..
34828         
34829         var colGroup = this._getColGroup( colSpan );
34830         // get the minimum Y value from the columns
34831         var minimumY = Math.min.apply( Math, colGroup );
34832         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34833         
34834         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34835          
34836         // position the brick
34837         var position = {
34838             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34839             y: this.currentSize.y + minimumY + this.padHeight
34840         };
34841         
34842         Roo.log(position);
34843         // apply setHeight to necessary columns
34844         var setHeight = minimumY + sz.height + this.padHeight;
34845         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34846         
34847         var setSpan = this.cols + 1 - colGroup.length;
34848         for ( var i = 0; i < setSpan; i++ ) {
34849           this.colYs[ shortColIndex + i ] = setHeight ;
34850         }
34851       
34852         return position;
34853     },
34854     
34855     /**
34856      * @param {Number} colSpan - number of columns the element spans
34857      * @returns {Array} colGroup
34858      */
34859     _getColGroup : function( colSpan )
34860     {
34861         if ( colSpan < 2 ) {
34862           // if brick spans only one column, use all the column Ys
34863           return this.colYs;
34864         }
34865       
34866         var colGroup = [];
34867         // how many different places could this brick fit horizontally
34868         var groupCount = this.cols + 1 - colSpan;
34869         // for each group potential horizontal position
34870         for ( var i = 0; i < groupCount; i++ ) {
34871           // make an array of colY values for that one group
34872           var groupColYs = this.colYs.slice( i, i + colSpan );
34873           // and get the max value of the array
34874           colGroup[i] = Math.max.apply( Math, groupColYs );
34875         }
34876         return colGroup;
34877     },
34878     /*
34879     _manageStamp : function( stamp )
34880     {
34881         var stampSize =  stamp.getSize();
34882         var offset = stamp.getBox();
34883         // get the columns that this stamp affects
34884         var firstX = this.isOriginLeft ? offset.x : offset.right;
34885         var lastX = firstX + stampSize.width;
34886         var firstCol = Math.floor( firstX / this.columnWidth );
34887         firstCol = Math.max( 0, firstCol );
34888         
34889         var lastCol = Math.floor( lastX / this.columnWidth );
34890         // lastCol should not go over if multiple of columnWidth #425
34891         lastCol -= lastX % this.columnWidth ? 0 : 1;
34892         lastCol = Math.min( this.cols - 1, lastCol );
34893         
34894         // set colYs to bottom of the stamp
34895         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34896             stampSize.height;
34897             
34898         for ( var i = firstCol; i <= lastCol; i++ ) {
34899           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34900         }
34901     },
34902     */
34903     
34904     _getContainerSize : function()
34905     {
34906         this.maxY = Math.max.apply( Math, this.colYs );
34907         var size = {
34908             height: this.maxY
34909         };
34910       
34911         if ( this.isFitWidth ) {
34912             size.width = this._getContainerFitWidth();
34913         }
34914       
34915         return size;
34916     },
34917     
34918     _getContainerFitWidth : function()
34919     {
34920         var unusedCols = 0;
34921         // count unused columns
34922         var i = this.cols;
34923         while ( --i ) {
34924           if ( this.colYs[i] !== 0 ) {
34925             break;
34926           }
34927           unusedCols++;
34928         }
34929         // fit container to columns that have been used
34930         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34931     },
34932     
34933     needsResizeLayout : function()
34934     {
34935         var previousWidth = this.containerWidth;
34936         this.getContainerWidth();
34937         return previousWidth !== this.containerWidth;
34938     }
34939  
34940 });
34941
34942  
34943
34944  /*
34945  * - LGPL
34946  *
34947  * element
34948  * 
34949  */
34950
34951 /**
34952  * @class Roo.bootstrap.MasonryBrick
34953  * @extends Roo.bootstrap.Component
34954  * Bootstrap MasonryBrick class
34955  * 
34956  * @constructor
34957  * Create a new MasonryBrick
34958  * @param {Object} config The config object
34959  */
34960
34961 Roo.bootstrap.MasonryBrick = function(config){
34962     
34963     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34964     
34965     Roo.bootstrap.MasonryBrick.register(this);
34966     
34967     this.addEvents({
34968         // raw events
34969         /**
34970          * @event click
34971          * When a MasonryBrick is clcik
34972          * @param {Roo.bootstrap.MasonryBrick} this
34973          * @param {Roo.EventObject} e
34974          */
34975         "click" : true
34976     });
34977 };
34978
34979 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34980     
34981     /**
34982      * @cfg {String} title
34983      */   
34984     title : '',
34985     /**
34986      * @cfg {String} html
34987      */   
34988     html : '',
34989     /**
34990      * @cfg {String} bgimage
34991      */   
34992     bgimage : '',
34993     /**
34994      * @cfg {String} videourl
34995      */   
34996     videourl : '',
34997     /**
34998      * @cfg {String} cls
34999      */   
35000     cls : '',
35001     /**
35002      * @cfg {String} href
35003      */   
35004     href : '',
35005     /**
35006      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35007      */   
35008     size : 'xs',
35009     
35010     /**
35011      * @cfg {String} placetitle (center|bottom)
35012      */   
35013     placetitle : '',
35014     
35015     /**
35016      * @cfg {Boolean} isFitContainer defalut true
35017      */   
35018     isFitContainer : true, 
35019     
35020     /**
35021      * @cfg {Boolean} preventDefault defalut false
35022      */   
35023     preventDefault : false, 
35024     
35025     /**
35026      * @cfg {Boolean} inverse defalut false
35027      */   
35028     maskInverse : false, 
35029     
35030     getAutoCreate : function()
35031     {
35032         if(!this.isFitContainer){
35033             return this.getSplitAutoCreate();
35034         }
35035         
35036         var cls = 'masonry-brick masonry-brick-full';
35037         
35038         if(this.href.length){
35039             cls += ' masonry-brick-link';
35040         }
35041         
35042         if(this.bgimage.length){
35043             cls += ' masonry-brick-image';
35044         }
35045         
35046         if(this.maskInverse){
35047             cls += ' mask-inverse';
35048         }
35049         
35050         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35051             cls += ' enable-mask';
35052         }
35053         
35054         if(this.size){
35055             cls += ' masonry-' + this.size + '-brick';
35056         }
35057         
35058         if(this.placetitle.length){
35059             
35060             switch (this.placetitle) {
35061                 case 'center' :
35062                     cls += ' masonry-center-title';
35063                     break;
35064                 case 'bottom' :
35065                     cls += ' masonry-bottom-title';
35066                     break;
35067                 default:
35068                     break;
35069             }
35070             
35071         } else {
35072             if(!this.html.length && !this.bgimage.length){
35073                 cls += ' masonry-center-title';
35074             }
35075
35076             if(!this.html.length && this.bgimage.length){
35077                 cls += ' masonry-bottom-title';
35078             }
35079         }
35080         
35081         if(this.cls){
35082             cls += ' ' + this.cls;
35083         }
35084         
35085         var cfg = {
35086             tag: (this.href.length) ? 'a' : 'div',
35087             cls: cls,
35088             cn: [
35089                 {
35090                     tag: 'div',
35091                     cls: 'masonry-brick-mask'
35092                 },
35093                 {
35094                     tag: 'div',
35095                     cls: 'masonry-brick-paragraph',
35096                     cn: []
35097                 }
35098             ]
35099         };
35100         
35101         if(this.href.length){
35102             cfg.href = this.href;
35103         }
35104         
35105         var cn = cfg.cn[1].cn;
35106         
35107         if(this.title.length){
35108             cn.push({
35109                 tag: 'h4',
35110                 cls: 'masonry-brick-title',
35111                 html: this.title
35112             });
35113         }
35114         
35115         if(this.html.length){
35116             cn.push({
35117                 tag: 'p',
35118                 cls: 'masonry-brick-text',
35119                 html: this.html
35120             });
35121         }
35122         
35123         if (!this.title.length && !this.html.length) {
35124             cfg.cn[1].cls += ' hide';
35125         }
35126         
35127         if(this.bgimage.length){
35128             cfg.cn.push({
35129                 tag: 'img',
35130                 cls: 'masonry-brick-image-view',
35131                 src: this.bgimage
35132             });
35133         }
35134         
35135         if(this.videourl.length){
35136             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35137             // youtube support only?
35138             cfg.cn.push({
35139                 tag: 'iframe',
35140                 cls: 'masonry-brick-image-view',
35141                 src: vurl,
35142                 frameborder : 0,
35143                 allowfullscreen : true
35144             });
35145         }
35146         
35147         return cfg;
35148         
35149     },
35150     
35151     getSplitAutoCreate : function()
35152     {
35153         var cls = 'masonry-brick masonry-brick-split';
35154         
35155         if(this.href.length){
35156             cls += ' masonry-brick-link';
35157         }
35158         
35159         if(this.bgimage.length){
35160             cls += ' masonry-brick-image';
35161         }
35162         
35163         if(this.size){
35164             cls += ' masonry-' + this.size + '-brick';
35165         }
35166         
35167         switch (this.placetitle) {
35168             case 'center' :
35169                 cls += ' masonry-center-title';
35170                 break;
35171             case 'bottom' :
35172                 cls += ' masonry-bottom-title';
35173                 break;
35174             default:
35175                 if(!this.bgimage.length){
35176                     cls += ' masonry-center-title';
35177                 }
35178
35179                 if(this.bgimage.length){
35180                     cls += ' masonry-bottom-title';
35181                 }
35182                 break;
35183         }
35184         
35185         if(this.cls){
35186             cls += ' ' + this.cls;
35187         }
35188         
35189         var cfg = {
35190             tag: (this.href.length) ? 'a' : 'div',
35191             cls: cls,
35192             cn: [
35193                 {
35194                     tag: 'div',
35195                     cls: 'masonry-brick-split-head',
35196                     cn: [
35197                         {
35198                             tag: 'div',
35199                             cls: 'masonry-brick-paragraph',
35200                             cn: []
35201                         }
35202                     ]
35203                 },
35204                 {
35205                     tag: 'div',
35206                     cls: 'masonry-brick-split-body',
35207                     cn: []
35208                 }
35209             ]
35210         };
35211         
35212         if(this.href.length){
35213             cfg.href = this.href;
35214         }
35215         
35216         if(this.title.length){
35217             cfg.cn[0].cn[0].cn.push({
35218                 tag: 'h4',
35219                 cls: 'masonry-brick-title',
35220                 html: this.title
35221             });
35222         }
35223         
35224         if(this.html.length){
35225             cfg.cn[1].cn.push({
35226                 tag: 'p',
35227                 cls: 'masonry-brick-text',
35228                 html: this.html
35229             });
35230         }
35231
35232         if(this.bgimage.length){
35233             cfg.cn[0].cn.push({
35234                 tag: 'img',
35235                 cls: 'masonry-brick-image-view',
35236                 src: this.bgimage
35237             });
35238         }
35239         
35240         if(this.videourl.length){
35241             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35242             // youtube support only?
35243             cfg.cn[0].cn.cn.push({
35244                 tag: 'iframe',
35245                 cls: 'masonry-brick-image-view',
35246                 src: vurl,
35247                 frameborder : 0,
35248                 allowfullscreen : true
35249             });
35250         }
35251         
35252         return cfg;
35253     },
35254     
35255     initEvents: function() 
35256     {
35257         switch (this.size) {
35258             case 'xs' :
35259                 this.x = 1;
35260                 this.y = 1;
35261                 break;
35262             case 'sm' :
35263                 this.x = 2;
35264                 this.y = 2;
35265                 break;
35266             case 'md' :
35267             case 'md-left' :
35268             case 'md-right' :
35269                 this.x = 3;
35270                 this.y = 3;
35271                 break;
35272             case 'tall' :
35273                 this.x = 2;
35274                 this.y = 3;
35275                 break;
35276             case 'wide' :
35277                 this.x = 3;
35278                 this.y = 2;
35279                 break;
35280             case 'wide-thin' :
35281                 this.x = 3;
35282                 this.y = 1;
35283                 break;
35284                         
35285             default :
35286                 break;
35287         }
35288         
35289         if(Roo.isTouch){
35290             this.el.on('touchstart', this.onTouchStart, this);
35291             this.el.on('touchmove', this.onTouchMove, this);
35292             this.el.on('touchend', this.onTouchEnd, this);
35293             this.el.on('contextmenu', this.onContextMenu, this);
35294         } else {
35295             this.el.on('mouseenter'  ,this.enter, this);
35296             this.el.on('mouseleave', this.leave, this);
35297             this.el.on('click', this.onClick, this);
35298         }
35299         
35300         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35301             this.parent().bricks.push(this);   
35302         }
35303         
35304     },
35305     
35306     onClick: function(e, el)
35307     {
35308         var time = this.endTimer - this.startTimer;
35309         // Roo.log(e.preventDefault());
35310         if(Roo.isTouch){
35311             if(time > 1000){
35312                 e.preventDefault();
35313                 return;
35314             }
35315         }
35316         
35317         if(!this.preventDefault){
35318             return;
35319         }
35320         
35321         e.preventDefault();
35322         
35323         if (this.activeClass != '') {
35324             this.selectBrick();
35325         }
35326         
35327         this.fireEvent('click', this, e);
35328     },
35329     
35330     enter: function(e, el)
35331     {
35332         e.preventDefault();
35333         
35334         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35335             return;
35336         }
35337         
35338         if(this.bgimage.length && this.html.length){
35339             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35340         }
35341     },
35342     
35343     leave: function(e, el)
35344     {
35345         e.preventDefault();
35346         
35347         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35348             return;
35349         }
35350         
35351         if(this.bgimage.length && this.html.length){
35352             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35353         }
35354     },
35355     
35356     onTouchStart: function(e, el)
35357     {
35358 //        e.preventDefault();
35359         
35360         this.touchmoved = false;
35361         
35362         if(!this.isFitContainer){
35363             return;
35364         }
35365         
35366         if(!this.bgimage.length || !this.html.length){
35367             return;
35368         }
35369         
35370         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35371         
35372         this.timer = new Date().getTime();
35373         
35374     },
35375     
35376     onTouchMove: function(e, el)
35377     {
35378         this.touchmoved = true;
35379     },
35380     
35381     onContextMenu : function(e,el)
35382     {
35383         e.preventDefault();
35384         e.stopPropagation();
35385         return false;
35386     },
35387     
35388     onTouchEnd: function(e, el)
35389     {
35390 //        e.preventDefault();
35391         
35392         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35393         
35394             this.leave(e,el);
35395             
35396             return;
35397         }
35398         
35399         if(!this.bgimage.length || !this.html.length){
35400             
35401             if(this.href.length){
35402                 window.location.href = this.href;
35403             }
35404             
35405             return;
35406         }
35407         
35408         if(!this.isFitContainer){
35409             return;
35410         }
35411         
35412         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35413         
35414         window.location.href = this.href;
35415     },
35416     
35417     //selection on single brick only
35418     selectBrick : function() {
35419         
35420         if (!this.parentId) {
35421             return;
35422         }
35423         
35424         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35425         var index = m.selectedBrick.indexOf(this.id);
35426         
35427         if ( index > -1) {
35428             m.selectedBrick.splice(index,1);
35429             this.el.removeClass(this.activeClass);
35430             return;
35431         }
35432         
35433         for(var i = 0; i < m.selectedBrick.length; i++) {
35434             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35435             b.el.removeClass(b.activeClass);
35436         }
35437         
35438         m.selectedBrick = [];
35439         
35440         m.selectedBrick.push(this.id);
35441         this.el.addClass(this.activeClass);
35442         return;
35443     },
35444     
35445     isSelected : function(){
35446         return this.el.hasClass(this.activeClass);
35447         
35448     }
35449 });
35450
35451 Roo.apply(Roo.bootstrap.MasonryBrick, {
35452     
35453     //groups: {},
35454     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35455      /**
35456     * register a Masonry Brick
35457     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35458     */
35459     
35460     register : function(brick)
35461     {
35462         //this.groups[brick.id] = brick;
35463         this.groups.add(brick.id, brick);
35464     },
35465     /**
35466     * fetch a  masonry brick based on the masonry brick ID
35467     * @param {string} the masonry brick to add
35468     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35469     */
35470     
35471     get: function(brick_id) 
35472     {
35473         // if (typeof(this.groups[brick_id]) == 'undefined') {
35474         //     return false;
35475         // }
35476         // return this.groups[brick_id] ;
35477         
35478         if(this.groups.key(brick_id)) {
35479             return this.groups.key(brick_id);
35480         }
35481         
35482         return false;
35483     }
35484     
35485     
35486     
35487 });
35488
35489  /*
35490  * - LGPL
35491  *
35492  * element
35493  * 
35494  */
35495
35496 /**
35497  * @class Roo.bootstrap.Brick
35498  * @extends Roo.bootstrap.Component
35499  * Bootstrap Brick class
35500  * 
35501  * @constructor
35502  * Create a new Brick
35503  * @param {Object} config The config object
35504  */
35505
35506 Roo.bootstrap.Brick = function(config){
35507     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35508     
35509     this.addEvents({
35510         // raw events
35511         /**
35512          * @event click
35513          * When a Brick is click
35514          * @param {Roo.bootstrap.Brick} this
35515          * @param {Roo.EventObject} e
35516          */
35517         "click" : true
35518     });
35519 };
35520
35521 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35522     
35523     /**
35524      * @cfg {String} title
35525      */   
35526     title : '',
35527     /**
35528      * @cfg {String} html
35529      */   
35530     html : '',
35531     /**
35532      * @cfg {String} bgimage
35533      */   
35534     bgimage : '',
35535     /**
35536      * @cfg {String} cls
35537      */   
35538     cls : '',
35539     /**
35540      * @cfg {String} href
35541      */   
35542     href : '',
35543     /**
35544      * @cfg {String} video
35545      */   
35546     video : '',
35547     /**
35548      * @cfg {Boolean} square
35549      */   
35550     square : true,
35551     
35552     getAutoCreate : function()
35553     {
35554         var cls = 'roo-brick';
35555         
35556         if(this.href.length){
35557             cls += ' roo-brick-link';
35558         }
35559         
35560         if(this.bgimage.length){
35561             cls += ' roo-brick-image';
35562         }
35563         
35564         if(!this.html.length && !this.bgimage.length){
35565             cls += ' roo-brick-center-title';
35566         }
35567         
35568         if(!this.html.length && this.bgimage.length){
35569             cls += ' roo-brick-bottom-title';
35570         }
35571         
35572         if(this.cls){
35573             cls += ' ' + this.cls;
35574         }
35575         
35576         var cfg = {
35577             tag: (this.href.length) ? 'a' : 'div',
35578             cls: cls,
35579             cn: [
35580                 {
35581                     tag: 'div',
35582                     cls: 'roo-brick-paragraph',
35583                     cn: []
35584                 }
35585             ]
35586         };
35587         
35588         if(this.href.length){
35589             cfg.href = this.href;
35590         }
35591         
35592         var cn = cfg.cn[0].cn;
35593         
35594         if(this.title.length){
35595             cn.push({
35596                 tag: 'h4',
35597                 cls: 'roo-brick-title',
35598                 html: this.title
35599             });
35600         }
35601         
35602         if(this.html.length){
35603             cn.push({
35604                 tag: 'p',
35605                 cls: 'roo-brick-text',
35606                 html: this.html
35607             });
35608         } else {
35609             cn.cls += ' hide';
35610         }
35611         
35612         if(this.bgimage.length){
35613             cfg.cn.push({
35614                 tag: 'img',
35615                 cls: 'roo-brick-image-view',
35616                 src: this.bgimage
35617             });
35618         }
35619         
35620         return cfg;
35621     },
35622     
35623     initEvents: function() 
35624     {
35625         if(this.title.length || this.html.length){
35626             this.el.on('mouseenter'  ,this.enter, this);
35627             this.el.on('mouseleave', this.leave, this);
35628         }
35629         
35630         Roo.EventManager.onWindowResize(this.resize, this); 
35631         
35632         if(this.bgimage.length){
35633             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35634             this.imageEl.on('load', this.onImageLoad, this);
35635             return;
35636         }
35637         
35638         this.resize();
35639     },
35640     
35641     onImageLoad : function()
35642     {
35643         this.resize();
35644     },
35645     
35646     resize : function()
35647     {
35648         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35649         
35650         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35651         
35652         if(this.bgimage.length){
35653             var image = this.el.select('.roo-brick-image-view', true).first();
35654             
35655             image.setWidth(paragraph.getWidth());
35656             
35657             if(this.square){
35658                 image.setHeight(paragraph.getWidth());
35659             }
35660             
35661             this.el.setHeight(image.getHeight());
35662             paragraph.setHeight(image.getHeight());
35663             
35664         }
35665         
35666     },
35667     
35668     enter: function(e, el)
35669     {
35670         e.preventDefault();
35671         
35672         if(this.bgimage.length){
35673             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35674             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35675         }
35676     },
35677     
35678     leave: function(e, el)
35679     {
35680         e.preventDefault();
35681         
35682         if(this.bgimage.length){
35683             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35684             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35685         }
35686     }
35687     
35688 });
35689
35690  
35691
35692  /*
35693  * - LGPL
35694  *
35695  * Number field 
35696  */
35697
35698 /**
35699  * @class Roo.bootstrap.NumberField
35700  * @extends Roo.bootstrap.Input
35701  * Bootstrap NumberField class
35702  * 
35703  * 
35704  * 
35705  * 
35706  * @constructor
35707  * Create a new NumberField
35708  * @param {Object} config The config object
35709  */
35710
35711 Roo.bootstrap.NumberField = function(config){
35712     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35713 };
35714
35715 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35716     
35717     /**
35718      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35719      */
35720     allowDecimals : true,
35721     /**
35722      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35723      */
35724     decimalSeparator : ".",
35725     /**
35726      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35727      */
35728     decimalPrecision : 2,
35729     /**
35730      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35731      */
35732     allowNegative : true,
35733     
35734     /**
35735      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35736      */
35737     allowZero: true,
35738     /**
35739      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35740      */
35741     minValue : Number.NEGATIVE_INFINITY,
35742     /**
35743      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35744      */
35745     maxValue : Number.MAX_VALUE,
35746     /**
35747      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35748      */
35749     minText : "The minimum value for this field is {0}",
35750     /**
35751      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35752      */
35753     maxText : "The maximum value for this field is {0}",
35754     /**
35755      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35756      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35757      */
35758     nanText : "{0} is not a valid number",
35759     /**
35760      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35761      */
35762     thousandsDelimiter : false,
35763     /**
35764      * @cfg {String} valueAlign alignment of value
35765      */
35766     valueAlign : "left",
35767
35768     getAutoCreate : function()
35769     {
35770         var hiddenInput = {
35771             tag: 'input',
35772             type: 'hidden',
35773             id: Roo.id(),
35774             cls: 'hidden-number-input'
35775         };
35776         
35777         if (this.name) {
35778             hiddenInput.name = this.name;
35779         }
35780         
35781         this.name = '';
35782         
35783         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35784         
35785         this.name = hiddenInput.name;
35786         
35787         if(cfg.cn.length > 0) {
35788             cfg.cn.push(hiddenInput);
35789         }
35790         
35791         return cfg;
35792     },
35793
35794     // private
35795     initEvents : function()
35796     {   
35797         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35798         
35799         var allowed = "0123456789";
35800         
35801         if(this.allowDecimals){
35802             allowed += this.decimalSeparator;
35803         }
35804         
35805         if(this.allowNegative){
35806             allowed += "-";
35807         }
35808         
35809         if(this.thousandsDelimiter) {
35810             allowed += ",";
35811         }
35812         
35813         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35814         
35815         var keyPress = function(e){
35816             
35817             var k = e.getKey();
35818             
35819             var c = e.getCharCode();
35820             
35821             if(
35822                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35823                     allowed.indexOf(String.fromCharCode(c)) === -1
35824             ){
35825                 e.stopEvent();
35826                 return;
35827             }
35828             
35829             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35830                 return;
35831             }
35832             
35833             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35834                 e.stopEvent();
35835             }
35836         };
35837         
35838         this.el.on("keypress", keyPress, this);
35839     },
35840     
35841     validateValue : function(value)
35842     {
35843         
35844         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35845             return false;
35846         }
35847         
35848         var num = this.parseValue(value);
35849         
35850         if(isNaN(num)){
35851             this.markInvalid(String.format(this.nanText, value));
35852             return false;
35853         }
35854         
35855         if(num < this.minValue){
35856             this.markInvalid(String.format(this.minText, this.minValue));
35857             return false;
35858         }
35859         
35860         if(num > this.maxValue){
35861             this.markInvalid(String.format(this.maxText, this.maxValue));
35862             return false;
35863         }
35864         
35865         return true;
35866     },
35867
35868     getValue : function()
35869     {
35870         var v = this.hiddenEl().getValue();
35871         
35872         return this.fixPrecision(this.parseValue(v));
35873     },
35874
35875     parseValue : function(value)
35876     {
35877         if(this.thousandsDelimiter) {
35878             value += "";
35879             r = new RegExp(",", "g");
35880             value = value.replace(r, "");
35881         }
35882         
35883         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35884         return isNaN(value) ? '' : value;
35885     },
35886
35887     fixPrecision : function(value)
35888     {
35889         if(this.thousandsDelimiter) {
35890             value += "";
35891             r = new RegExp(",", "g");
35892             value = value.replace(r, "");
35893         }
35894         
35895         var nan = isNaN(value);
35896         
35897         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35898             return nan ? '' : value;
35899         }
35900         return parseFloat(value).toFixed(this.decimalPrecision);
35901     },
35902
35903     setValue : function(v)
35904     {
35905         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35906         
35907         this.value = v;
35908         
35909         if(this.rendered){
35910             
35911             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35912             
35913             this.inputEl().dom.value = (v == '') ? '' :
35914                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35915             
35916             if(!this.allowZero && v === '0') {
35917                 this.hiddenEl().dom.value = '';
35918                 this.inputEl().dom.value = '';
35919             }
35920             
35921             this.validate();
35922         }
35923     },
35924
35925     decimalPrecisionFcn : function(v)
35926     {
35927         return Math.floor(v);
35928     },
35929
35930     beforeBlur : function()
35931     {
35932         var v = this.parseValue(this.getRawValue());
35933         
35934         if(v || v === 0 || v === ''){
35935             this.setValue(v);
35936         }
35937     },
35938     
35939     hiddenEl : function()
35940     {
35941         return this.el.select('input.hidden-number-input',true).first();
35942     }
35943     
35944 });
35945
35946  
35947
35948 /*
35949 * Licence: LGPL
35950 */
35951
35952 /**
35953  * @class Roo.bootstrap.DocumentSlider
35954  * @extends Roo.bootstrap.Component
35955  * Bootstrap DocumentSlider class
35956  * 
35957  * @constructor
35958  * Create a new DocumentViewer
35959  * @param {Object} config The config object
35960  */
35961
35962 Roo.bootstrap.DocumentSlider = function(config){
35963     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35964     
35965     this.files = [];
35966     
35967     this.addEvents({
35968         /**
35969          * @event initial
35970          * Fire after initEvent
35971          * @param {Roo.bootstrap.DocumentSlider} this
35972          */
35973         "initial" : true,
35974         /**
35975          * @event update
35976          * Fire after update
35977          * @param {Roo.bootstrap.DocumentSlider} this
35978          */
35979         "update" : true,
35980         /**
35981          * @event click
35982          * Fire after click
35983          * @param {Roo.bootstrap.DocumentSlider} this
35984          */
35985         "click" : true
35986     });
35987 };
35988
35989 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35990     
35991     files : false,
35992     
35993     indicator : 0,
35994     
35995     getAutoCreate : function()
35996     {
35997         var cfg = {
35998             tag : 'div',
35999             cls : 'roo-document-slider',
36000             cn : [
36001                 {
36002                     tag : 'div',
36003                     cls : 'roo-document-slider-header',
36004                     cn : [
36005                         {
36006                             tag : 'div',
36007                             cls : 'roo-document-slider-header-title'
36008                         }
36009                     ]
36010                 },
36011                 {
36012                     tag : 'div',
36013                     cls : 'roo-document-slider-body',
36014                     cn : [
36015                         {
36016                             tag : 'div',
36017                             cls : 'roo-document-slider-prev',
36018                             cn : [
36019                                 {
36020                                     tag : 'i',
36021                                     cls : 'fa fa-chevron-left'
36022                                 }
36023                             ]
36024                         },
36025                         {
36026                             tag : 'div',
36027                             cls : 'roo-document-slider-thumb',
36028                             cn : [
36029                                 {
36030                                     tag : 'img',
36031                                     cls : 'roo-document-slider-image'
36032                                 }
36033                             ]
36034                         },
36035                         {
36036                             tag : 'div',
36037                             cls : 'roo-document-slider-next',
36038                             cn : [
36039                                 {
36040                                     tag : 'i',
36041                                     cls : 'fa fa-chevron-right'
36042                                 }
36043                             ]
36044                         }
36045                     ]
36046                 }
36047             ]
36048         };
36049         
36050         return cfg;
36051     },
36052     
36053     initEvents : function()
36054     {
36055         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36056         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36057         
36058         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36059         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36060         
36061         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36062         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36063         
36064         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36065         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36066         
36067         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36068         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36069         
36070         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36071         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36072         
36073         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36074         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36075         
36076         this.thumbEl.on('click', this.onClick, this);
36077         
36078         this.prevIndicator.on('click', this.prev, this);
36079         
36080         this.nextIndicator.on('click', this.next, this);
36081         
36082     },
36083     
36084     initial : function()
36085     {
36086         if(this.files.length){
36087             this.indicator = 1;
36088             this.update()
36089         }
36090         
36091         this.fireEvent('initial', this);
36092     },
36093     
36094     update : function()
36095     {
36096         this.imageEl.attr('src', this.files[this.indicator - 1]);
36097         
36098         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36099         
36100         this.prevIndicator.show();
36101         
36102         if(this.indicator == 1){
36103             this.prevIndicator.hide();
36104         }
36105         
36106         this.nextIndicator.show();
36107         
36108         if(this.indicator == this.files.length){
36109             this.nextIndicator.hide();
36110         }
36111         
36112         this.thumbEl.scrollTo('top');
36113         
36114         this.fireEvent('update', this);
36115     },
36116     
36117     onClick : function(e)
36118     {
36119         e.preventDefault();
36120         
36121         this.fireEvent('click', this);
36122     },
36123     
36124     prev : function(e)
36125     {
36126         e.preventDefault();
36127         
36128         this.indicator = Math.max(1, this.indicator - 1);
36129         
36130         this.update();
36131     },
36132     
36133     next : function(e)
36134     {
36135         e.preventDefault();
36136         
36137         this.indicator = Math.min(this.files.length, this.indicator + 1);
36138         
36139         this.update();
36140     }
36141 });
36142 /*
36143  * - LGPL
36144  *
36145  * RadioSet
36146  *
36147  *
36148  */
36149
36150 /**
36151  * @class Roo.bootstrap.RadioSet
36152  * @extends Roo.bootstrap.Input
36153  * Bootstrap RadioSet class
36154  * @cfg {String} indicatorpos (left|right) default left
36155  * @cfg {Boolean} inline (true|false) inline the element (default true)
36156  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36157  * @constructor
36158  * Create a new RadioSet
36159  * @param {Object} config The config object
36160  */
36161
36162 Roo.bootstrap.RadioSet = function(config){
36163     
36164     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36165     
36166     this.radioes = [];
36167     
36168     Roo.bootstrap.RadioSet.register(this);
36169     
36170     this.addEvents({
36171         /**
36172         * @event check
36173         * Fires when the element is checked or unchecked.
36174         * @param {Roo.bootstrap.RadioSet} this This radio
36175         * @param {Roo.bootstrap.Radio} item The checked item
36176         */
36177        check : true,
36178        /**
36179         * @event click
36180         * Fires when the element is click.
36181         * @param {Roo.bootstrap.RadioSet} this This radio set
36182         * @param {Roo.bootstrap.Radio} item The checked item
36183         * @param {Roo.EventObject} e The event object
36184         */
36185        click : true
36186     });
36187     
36188 };
36189
36190 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36191
36192     radioes : false,
36193     
36194     inline : true,
36195     
36196     weight : '',
36197     
36198     indicatorpos : 'left',
36199     
36200     getAutoCreate : function()
36201     {
36202         var label = {
36203             tag : 'label',
36204             cls : 'roo-radio-set-label',
36205             cn : [
36206                 {
36207                     tag : 'span',
36208                     html : this.fieldLabel
36209                 }
36210             ]
36211         };
36212         if (Roo.bootstrap.version == 3) {
36213             
36214             
36215             if(this.indicatorpos == 'left'){
36216                 label.cn.unshift({
36217                     tag : 'i',
36218                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36219                     tooltip : 'This field is required'
36220                 });
36221             } else {
36222                 label.cn.push({
36223                     tag : 'i',
36224                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36225                     tooltip : 'This field is required'
36226                 });
36227             }
36228         }
36229         var items = {
36230             tag : 'div',
36231             cls : 'roo-radio-set-items'
36232         };
36233         
36234         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36235         
36236         if (align === 'left' && this.fieldLabel.length) {
36237             
36238             items = {
36239                 cls : "roo-radio-set-right", 
36240                 cn: [
36241                     items
36242                 ]
36243             };
36244             
36245             if(this.labelWidth > 12){
36246                 label.style = "width: " + this.labelWidth + 'px';
36247             }
36248             
36249             if(this.labelWidth < 13 && this.labelmd == 0){
36250                 this.labelmd = this.labelWidth;
36251             }
36252             
36253             if(this.labellg > 0){
36254                 label.cls += ' col-lg-' + this.labellg;
36255                 items.cls += ' col-lg-' + (12 - this.labellg);
36256             }
36257             
36258             if(this.labelmd > 0){
36259                 label.cls += ' col-md-' + this.labelmd;
36260                 items.cls += ' col-md-' + (12 - this.labelmd);
36261             }
36262             
36263             if(this.labelsm > 0){
36264                 label.cls += ' col-sm-' + this.labelsm;
36265                 items.cls += ' col-sm-' + (12 - this.labelsm);
36266             }
36267             
36268             if(this.labelxs > 0){
36269                 label.cls += ' col-xs-' + this.labelxs;
36270                 items.cls += ' col-xs-' + (12 - this.labelxs);
36271             }
36272         }
36273         
36274         var cfg = {
36275             tag : 'div',
36276             cls : 'roo-radio-set',
36277             cn : [
36278                 {
36279                     tag : 'input',
36280                     cls : 'roo-radio-set-input',
36281                     type : 'hidden',
36282                     name : this.name,
36283                     value : this.value ? this.value :  ''
36284                 },
36285                 label,
36286                 items
36287             ]
36288         };
36289         
36290         if(this.weight.length){
36291             cfg.cls += ' roo-radio-' + this.weight;
36292         }
36293         
36294         if(this.inline) {
36295             cfg.cls += ' roo-radio-set-inline';
36296         }
36297         
36298         var settings=this;
36299         ['xs','sm','md','lg'].map(function(size){
36300             if (settings[size]) {
36301                 cfg.cls += ' col-' + size + '-' + settings[size];
36302             }
36303         });
36304         
36305         return cfg;
36306         
36307     },
36308
36309     initEvents : function()
36310     {
36311         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36312         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36313         
36314         if(!this.fieldLabel.length){
36315             this.labelEl.hide();
36316         }
36317         
36318         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36319         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36320         
36321         this.indicator = this.indicatorEl();
36322         
36323         if(this.indicator){
36324             this.indicator.addClass('invisible');
36325         }
36326         
36327         this.originalValue = this.getValue();
36328         
36329     },
36330     
36331     inputEl: function ()
36332     {
36333         return this.el.select('.roo-radio-set-input', true).first();
36334     },
36335     
36336     getChildContainer : function()
36337     {
36338         return this.itemsEl;
36339     },
36340     
36341     register : function(item)
36342     {
36343         this.radioes.push(item);
36344         
36345     },
36346     
36347     validate : function()
36348     {   
36349         if(this.getVisibilityEl().hasClass('hidden')){
36350             return true;
36351         }
36352         
36353         var valid = false;
36354         
36355         Roo.each(this.radioes, function(i){
36356             if(!i.checked){
36357                 return;
36358             }
36359             
36360             valid = true;
36361             return false;
36362         });
36363         
36364         if(this.allowBlank) {
36365             return true;
36366         }
36367         
36368         if(this.disabled || valid){
36369             this.markValid();
36370             return true;
36371         }
36372         
36373         this.markInvalid();
36374         return false;
36375         
36376     },
36377     
36378     markValid : function()
36379     {
36380         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36381             this.indicatorEl().removeClass('visible');
36382             this.indicatorEl().addClass('invisible');
36383         }
36384         
36385         
36386         if (Roo.bootstrap.version == 3) {
36387             this.el.removeClass([this.invalidClass, this.validClass]);
36388             this.el.addClass(this.validClass);
36389         } else {
36390             this.el.removeClass(['is-invalid','is-valid']);
36391             this.el.addClass(['is-valid']);
36392         }
36393         this.fireEvent('valid', this);
36394     },
36395     
36396     markInvalid : function(msg)
36397     {
36398         if(this.allowBlank || this.disabled){
36399             return;
36400         }
36401         
36402         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36403             this.indicatorEl().removeClass('invisible');
36404             this.indicatorEl().addClass('visible');
36405         }
36406         if (Roo.bootstrap.version == 3) {
36407             this.el.removeClass([this.invalidClass, this.validClass]);
36408             this.el.addClass(this.invalidClass);
36409         } else {
36410             this.el.removeClass(['is-invalid','is-valid']);
36411             this.el.addClass(['is-invalid']);
36412         }
36413         
36414         this.fireEvent('invalid', this, msg);
36415         
36416     },
36417     
36418     setValue : function(v, suppressEvent)
36419     {   
36420         if(this.value === v){
36421             return;
36422         }
36423         
36424         this.value = v;
36425         
36426         if(this.rendered){
36427             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36428         }
36429         
36430         Roo.each(this.radioes, function(i){
36431             i.checked = false;
36432             i.el.removeClass('checked');
36433         });
36434         
36435         Roo.each(this.radioes, function(i){
36436             
36437             if(i.value === v || i.value.toString() === v.toString()){
36438                 i.checked = true;
36439                 i.el.addClass('checked');
36440                 
36441                 if(suppressEvent !== true){
36442                     this.fireEvent('check', this, i);
36443                 }
36444                 
36445                 return false;
36446             }
36447             
36448         }, this);
36449         
36450         this.validate();
36451     },
36452     
36453     clearInvalid : function(){
36454         
36455         if(!this.el || this.preventMark){
36456             return;
36457         }
36458         
36459         this.el.removeClass([this.invalidClass]);
36460         
36461         this.fireEvent('valid', this);
36462     }
36463     
36464 });
36465
36466 Roo.apply(Roo.bootstrap.RadioSet, {
36467     
36468     groups: {},
36469     
36470     register : function(set)
36471     {
36472         this.groups[set.name] = set;
36473     },
36474     
36475     get: function(name) 
36476     {
36477         if (typeof(this.groups[name]) == 'undefined') {
36478             return false;
36479         }
36480         
36481         return this.groups[name] ;
36482     }
36483     
36484 });
36485 /*
36486  * Based on:
36487  * Ext JS Library 1.1.1
36488  * Copyright(c) 2006-2007, Ext JS, LLC.
36489  *
36490  * Originally Released Under LGPL - original licence link has changed is not relivant.
36491  *
36492  * Fork - LGPL
36493  * <script type="text/javascript">
36494  */
36495
36496
36497 /**
36498  * @class Roo.bootstrap.SplitBar
36499  * @extends Roo.util.Observable
36500  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36501  * <br><br>
36502  * Usage:
36503  * <pre><code>
36504 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36505                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36506 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36507 split.minSize = 100;
36508 split.maxSize = 600;
36509 split.animate = true;
36510 split.on('moved', splitterMoved);
36511 </code></pre>
36512  * @constructor
36513  * Create a new SplitBar
36514  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36515  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36516  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36517  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36518                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36519                         position of the SplitBar).
36520  */
36521 Roo.bootstrap.SplitBar = function(cfg){
36522     
36523     /** @private */
36524     
36525     //{
36526     //  dragElement : elm
36527     //  resizingElement: el,
36528         // optional..
36529     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36530     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36531         // existingProxy ???
36532     //}
36533     
36534     this.el = Roo.get(cfg.dragElement, true);
36535     this.el.dom.unselectable = "on";
36536     /** @private */
36537     this.resizingEl = Roo.get(cfg.resizingElement, true);
36538
36539     /**
36540      * @private
36541      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36542      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36543      * @type Number
36544      */
36545     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36546     
36547     /**
36548      * The minimum size of the resizing element. (Defaults to 0)
36549      * @type Number
36550      */
36551     this.minSize = 0;
36552     
36553     /**
36554      * The maximum size of the resizing element. (Defaults to 2000)
36555      * @type Number
36556      */
36557     this.maxSize = 2000;
36558     
36559     /**
36560      * Whether to animate the transition to the new size
36561      * @type Boolean
36562      */
36563     this.animate = false;
36564     
36565     /**
36566      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36567      * @type Boolean
36568      */
36569     this.useShim = false;
36570     
36571     /** @private */
36572     this.shim = null;
36573     
36574     if(!cfg.existingProxy){
36575         /** @private */
36576         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36577     }else{
36578         this.proxy = Roo.get(cfg.existingProxy).dom;
36579     }
36580     /** @private */
36581     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36582     
36583     /** @private */
36584     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36585     
36586     /** @private */
36587     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36588     
36589     /** @private */
36590     this.dragSpecs = {};
36591     
36592     /**
36593      * @private The adapter to use to positon and resize elements
36594      */
36595     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36596     this.adapter.init(this);
36597     
36598     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36599         /** @private */
36600         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36601         this.el.addClass("roo-splitbar-h");
36602     }else{
36603         /** @private */
36604         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36605         this.el.addClass("roo-splitbar-v");
36606     }
36607     
36608     this.addEvents({
36609         /**
36610          * @event resize
36611          * Fires when the splitter is moved (alias for {@link #event-moved})
36612          * @param {Roo.bootstrap.SplitBar} this
36613          * @param {Number} newSize the new width or height
36614          */
36615         "resize" : true,
36616         /**
36617          * @event moved
36618          * Fires when the splitter is moved
36619          * @param {Roo.bootstrap.SplitBar} this
36620          * @param {Number} newSize the new width or height
36621          */
36622         "moved" : true,
36623         /**
36624          * @event beforeresize
36625          * Fires before the splitter is dragged
36626          * @param {Roo.bootstrap.SplitBar} this
36627          */
36628         "beforeresize" : true,
36629
36630         "beforeapply" : true
36631     });
36632
36633     Roo.util.Observable.call(this);
36634 };
36635
36636 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36637     onStartProxyDrag : function(x, y){
36638         this.fireEvent("beforeresize", this);
36639         if(!this.overlay){
36640             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36641             o.unselectable();
36642             o.enableDisplayMode("block");
36643             // all splitbars share the same overlay
36644             Roo.bootstrap.SplitBar.prototype.overlay = o;
36645         }
36646         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36647         this.overlay.show();
36648         Roo.get(this.proxy).setDisplayed("block");
36649         var size = this.adapter.getElementSize(this);
36650         this.activeMinSize = this.getMinimumSize();;
36651         this.activeMaxSize = this.getMaximumSize();;
36652         var c1 = size - this.activeMinSize;
36653         var c2 = Math.max(this.activeMaxSize - size, 0);
36654         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36655             this.dd.resetConstraints();
36656             this.dd.setXConstraint(
36657                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36658                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36659             );
36660             this.dd.setYConstraint(0, 0);
36661         }else{
36662             this.dd.resetConstraints();
36663             this.dd.setXConstraint(0, 0);
36664             this.dd.setYConstraint(
36665                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36666                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36667             );
36668          }
36669         this.dragSpecs.startSize = size;
36670         this.dragSpecs.startPoint = [x, y];
36671         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36672     },
36673     
36674     /** 
36675      * @private Called after the drag operation by the DDProxy
36676      */
36677     onEndProxyDrag : function(e){
36678         Roo.get(this.proxy).setDisplayed(false);
36679         var endPoint = Roo.lib.Event.getXY(e);
36680         if(this.overlay){
36681             this.overlay.hide();
36682         }
36683         var newSize;
36684         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36685             newSize = this.dragSpecs.startSize + 
36686                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36687                     endPoint[0] - this.dragSpecs.startPoint[0] :
36688                     this.dragSpecs.startPoint[0] - endPoint[0]
36689                 );
36690         }else{
36691             newSize = this.dragSpecs.startSize + 
36692                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36693                     endPoint[1] - this.dragSpecs.startPoint[1] :
36694                     this.dragSpecs.startPoint[1] - endPoint[1]
36695                 );
36696         }
36697         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36698         if(newSize != this.dragSpecs.startSize){
36699             if(this.fireEvent('beforeapply', this, newSize) !== false){
36700                 this.adapter.setElementSize(this, newSize);
36701                 this.fireEvent("moved", this, newSize);
36702                 this.fireEvent("resize", this, newSize);
36703             }
36704         }
36705     },
36706     
36707     /**
36708      * Get the adapter this SplitBar uses
36709      * @return The adapter object
36710      */
36711     getAdapter : function(){
36712         return this.adapter;
36713     },
36714     
36715     /**
36716      * Set the adapter this SplitBar uses
36717      * @param {Object} adapter A SplitBar adapter object
36718      */
36719     setAdapter : function(adapter){
36720         this.adapter = adapter;
36721         this.adapter.init(this);
36722     },
36723     
36724     /**
36725      * Gets the minimum size for the resizing element
36726      * @return {Number} The minimum size
36727      */
36728     getMinimumSize : function(){
36729         return this.minSize;
36730     },
36731     
36732     /**
36733      * Sets the minimum size for the resizing element
36734      * @param {Number} minSize The minimum size
36735      */
36736     setMinimumSize : function(minSize){
36737         this.minSize = minSize;
36738     },
36739     
36740     /**
36741      * Gets the maximum size for the resizing element
36742      * @return {Number} The maximum size
36743      */
36744     getMaximumSize : function(){
36745         return this.maxSize;
36746     },
36747     
36748     /**
36749      * Sets the maximum size for the resizing element
36750      * @param {Number} maxSize The maximum size
36751      */
36752     setMaximumSize : function(maxSize){
36753         this.maxSize = maxSize;
36754     },
36755     
36756     /**
36757      * Sets the initialize size for the resizing element
36758      * @param {Number} size The initial size
36759      */
36760     setCurrentSize : function(size){
36761         var oldAnimate = this.animate;
36762         this.animate = false;
36763         this.adapter.setElementSize(this, size);
36764         this.animate = oldAnimate;
36765     },
36766     
36767     /**
36768      * Destroy this splitbar. 
36769      * @param {Boolean} removeEl True to remove the element
36770      */
36771     destroy : function(removeEl){
36772         if(this.shim){
36773             this.shim.remove();
36774         }
36775         this.dd.unreg();
36776         this.proxy.parentNode.removeChild(this.proxy);
36777         if(removeEl){
36778             this.el.remove();
36779         }
36780     }
36781 });
36782
36783 /**
36784  * @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.
36785  */
36786 Roo.bootstrap.SplitBar.createProxy = function(dir){
36787     var proxy = new Roo.Element(document.createElement("div"));
36788     proxy.unselectable();
36789     var cls = 'roo-splitbar-proxy';
36790     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36791     document.body.appendChild(proxy.dom);
36792     return proxy.dom;
36793 };
36794
36795 /** 
36796  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36797  * Default Adapter. It assumes the splitter and resizing element are not positioned
36798  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36799  */
36800 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36801 };
36802
36803 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36804     // do nothing for now
36805     init : function(s){
36806     
36807     },
36808     /**
36809      * Called before drag operations to get the current size of the resizing element. 
36810      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36811      */
36812      getElementSize : function(s){
36813         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36814             return s.resizingEl.getWidth();
36815         }else{
36816             return s.resizingEl.getHeight();
36817         }
36818     },
36819     
36820     /**
36821      * Called after drag operations to set the size of the resizing element.
36822      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36823      * @param {Number} newSize The new size to set
36824      * @param {Function} onComplete A function to be invoked when resizing is complete
36825      */
36826     setElementSize : function(s, newSize, onComplete){
36827         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36828             if(!s.animate){
36829                 s.resizingEl.setWidth(newSize);
36830                 if(onComplete){
36831                     onComplete(s, newSize);
36832                 }
36833             }else{
36834                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36835             }
36836         }else{
36837             
36838             if(!s.animate){
36839                 s.resizingEl.setHeight(newSize);
36840                 if(onComplete){
36841                     onComplete(s, newSize);
36842                 }
36843             }else{
36844                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36845             }
36846         }
36847     }
36848 };
36849
36850 /** 
36851  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36852  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36853  * Adapter that  moves the splitter element to align with the resized sizing element. 
36854  * Used with an absolute positioned SplitBar.
36855  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36856  * document.body, make sure you assign an id to the body element.
36857  */
36858 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36859     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36860     this.container = Roo.get(container);
36861 };
36862
36863 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36864     init : function(s){
36865         this.basic.init(s);
36866     },
36867     
36868     getElementSize : function(s){
36869         return this.basic.getElementSize(s);
36870     },
36871     
36872     setElementSize : function(s, newSize, onComplete){
36873         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36874     },
36875     
36876     moveSplitter : function(s){
36877         var yes = Roo.bootstrap.SplitBar;
36878         switch(s.placement){
36879             case yes.LEFT:
36880                 s.el.setX(s.resizingEl.getRight());
36881                 break;
36882             case yes.RIGHT:
36883                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36884                 break;
36885             case yes.TOP:
36886                 s.el.setY(s.resizingEl.getBottom());
36887                 break;
36888             case yes.BOTTOM:
36889                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36890                 break;
36891         }
36892     }
36893 };
36894
36895 /**
36896  * Orientation constant - Create a vertical SplitBar
36897  * @static
36898  * @type Number
36899  */
36900 Roo.bootstrap.SplitBar.VERTICAL = 1;
36901
36902 /**
36903  * Orientation constant - Create a horizontal SplitBar
36904  * @static
36905  * @type Number
36906  */
36907 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36908
36909 /**
36910  * Placement constant - The resizing element is to the left of the splitter element
36911  * @static
36912  * @type Number
36913  */
36914 Roo.bootstrap.SplitBar.LEFT = 1;
36915
36916 /**
36917  * Placement constant - The resizing element is to the right of the splitter element
36918  * @static
36919  * @type Number
36920  */
36921 Roo.bootstrap.SplitBar.RIGHT = 2;
36922
36923 /**
36924  * Placement constant - The resizing element is positioned above the splitter element
36925  * @static
36926  * @type Number
36927  */
36928 Roo.bootstrap.SplitBar.TOP = 3;
36929
36930 /**
36931  * Placement constant - The resizing element is positioned under splitter element
36932  * @static
36933  * @type Number
36934  */
36935 Roo.bootstrap.SplitBar.BOTTOM = 4;
36936 Roo.namespace("Roo.bootstrap.layout");/*
36937  * Based on:
36938  * Ext JS Library 1.1.1
36939  * Copyright(c) 2006-2007, Ext JS, LLC.
36940  *
36941  * Originally Released Under LGPL - original licence link has changed is not relivant.
36942  *
36943  * Fork - LGPL
36944  * <script type="text/javascript">
36945  */
36946
36947 /**
36948  * @class Roo.bootstrap.layout.Manager
36949  * @extends Roo.bootstrap.Component
36950  * Base class for layout managers.
36951  */
36952 Roo.bootstrap.layout.Manager = function(config)
36953 {
36954     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36955
36956
36957
36958
36959
36960     /** false to disable window resize monitoring @type Boolean */
36961     this.monitorWindowResize = true;
36962     this.regions = {};
36963     this.addEvents({
36964         /**
36965          * @event layout
36966          * Fires when a layout is performed.
36967          * @param {Roo.LayoutManager} this
36968          */
36969         "layout" : true,
36970         /**
36971          * @event regionresized
36972          * Fires when the user resizes a region.
36973          * @param {Roo.LayoutRegion} region The resized region
36974          * @param {Number} newSize The new size (width for east/west, height for north/south)
36975          */
36976         "regionresized" : true,
36977         /**
36978          * @event regioncollapsed
36979          * Fires when a region is collapsed.
36980          * @param {Roo.LayoutRegion} region The collapsed region
36981          */
36982         "regioncollapsed" : true,
36983         /**
36984          * @event regionexpanded
36985          * Fires when a region is expanded.
36986          * @param {Roo.LayoutRegion} region The expanded region
36987          */
36988         "regionexpanded" : true
36989     });
36990     this.updating = false;
36991
36992     if (config.el) {
36993         this.el = Roo.get(config.el);
36994         this.initEvents();
36995     }
36996
36997 };
36998
36999 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37000
37001
37002     regions : null,
37003
37004     monitorWindowResize : true,
37005
37006
37007     updating : false,
37008
37009
37010     onRender : function(ct, position)
37011     {
37012         if(!this.el){
37013             this.el = Roo.get(ct);
37014             this.initEvents();
37015         }
37016         //this.fireEvent('render',this);
37017     },
37018
37019
37020     initEvents: function()
37021     {
37022
37023
37024         // ie scrollbar fix
37025         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37026             document.body.scroll = "no";
37027         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37028             this.el.position('relative');
37029         }
37030         this.id = this.el.id;
37031         this.el.addClass("roo-layout-container");
37032         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37033         if(this.el.dom != document.body ) {
37034             this.el.on('resize', this.layout,this);
37035             this.el.on('show', this.layout,this);
37036         }
37037
37038     },
37039
37040     /**
37041      * Returns true if this layout is currently being updated
37042      * @return {Boolean}
37043      */
37044     isUpdating : function(){
37045         return this.updating;
37046     },
37047
37048     /**
37049      * Suspend the LayoutManager from doing auto-layouts while
37050      * making multiple add or remove calls
37051      */
37052     beginUpdate : function(){
37053         this.updating = true;
37054     },
37055
37056     /**
37057      * Restore auto-layouts and optionally disable the manager from performing a layout
37058      * @param {Boolean} noLayout true to disable a layout update
37059      */
37060     endUpdate : function(noLayout){
37061         this.updating = false;
37062         if(!noLayout){
37063             this.layout();
37064         }
37065     },
37066
37067     layout: function(){
37068         // abstract...
37069     },
37070
37071     onRegionResized : function(region, newSize){
37072         this.fireEvent("regionresized", region, newSize);
37073         this.layout();
37074     },
37075
37076     onRegionCollapsed : function(region){
37077         this.fireEvent("regioncollapsed", region);
37078     },
37079
37080     onRegionExpanded : function(region){
37081         this.fireEvent("regionexpanded", region);
37082     },
37083
37084     /**
37085      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37086      * performs box-model adjustments.
37087      * @return {Object} The size as an object {width: (the width), height: (the height)}
37088      */
37089     getViewSize : function()
37090     {
37091         var size;
37092         if(this.el.dom != document.body){
37093             size = this.el.getSize();
37094         }else{
37095             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37096         }
37097         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37098         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37099         return size;
37100     },
37101
37102     /**
37103      * Returns the Element this layout is bound to.
37104      * @return {Roo.Element}
37105      */
37106     getEl : function(){
37107         return this.el;
37108     },
37109
37110     /**
37111      * Returns the specified region.
37112      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37113      * @return {Roo.LayoutRegion}
37114      */
37115     getRegion : function(target){
37116         return this.regions[target.toLowerCase()];
37117     },
37118
37119     onWindowResize : function(){
37120         if(this.monitorWindowResize){
37121             this.layout();
37122         }
37123     }
37124 });
37125 /*
37126  * Based on:
37127  * Ext JS Library 1.1.1
37128  * Copyright(c) 2006-2007, Ext JS, LLC.
37129  *
37130  * Originally Released Under LGPL - original licence link has changed is not relivant.
37131  *
37132  * Fork - LGPL
37133  * <script type="text/javascript">
37134  */
37135 /**
37136  * @class Roo.bootstrap.layout.Border
37137  * @extends Roo.bootstrap.layout.Manager
37138  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37139  * please see: examples/bootstrap/nested.html<br><br>
37140  
37141 <b>The container the layout is rendered into can be either the body element or any other element.
37142 If it is not the body element, the container needs to either be an absolute positioned element,
37143 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37144 the container size if it is not the body element.</b>
37145
37146 * @constructor
37147 * Create a new Border
37148 * @param {Object} config Configuration options
37149  */
37150 Roo.bootstrap.layout.Border = function(config){
37151     config = config || {};
37152     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37153     
37154     
37155     
37156     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37157         if(config[region]){
37158             config[region].region = region;
37159             this.addRegion(config[region]);
37160         }
37161     },this);
37162     
37163 };
37164
37165 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37166
37167 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37168     
37169     parent : false, // this might point to a 'nest' or a ???
37170     
37171     /**
37172      * Creates and adds a new region if it doesn't already exist.
37173      * @param {String} target The target region key (north, south, east, west or center).
37174      * @param {Object} config The regions config object
37175      * @return {BorderLayoutRegion} The new region
37176      */
37177     addRegion : function(config)
37178     {
37179         if(!this.regions[config.region]){
37180             var r = this.factory(config);
37181             this.bindRegion(r);
37182         }
37183         return this.regions[config.region];
37184     },
37185
37186     // private (kinda)
37187     bindRegion : function(r){
37188         this.regions[r.config.region] = r;
37189         
37190         r.on("visibilitychange",    this.layout, this);
37191         r.on("paneladded",          this.layout, this);
37192         r.on("panelremoved",        this.layout, this);
37193         r.on("invalidated",         this.layout, this);
37194         r.on("resized",             this.onRegionResized, this);
37195         r.on("collapsed",           this.onRegionCollapsed, this);
37196         r.on("expanded",            this.onRegionExpanded, this);
37197     },
37198
37199     /**
37200      * Performs a layout update.
37201      */
37202     layout : function()
37203     {
37204         if(this.updating) {
37205             return;
37206         }
37207         
37208         // render all the rebions if they have not been done alreayd?
37209         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37210             if(this.regions[region] && !this.regions[region].bodyEl){
37211                 this.regions[region].onRender(this.el)
37212             }
37213         },this);
37214         
37215         var size = this.getViewSize();
37216         var w = size.width;
37217         var h = size.height;
37218         var centerW = w;
37219         var centerH = h;
37220         var centerY = 0;
37221         var centerX = 0;
37222         //var x = 0, y = 0;
37223
37224         var rs = this.regions;
37225         var north = rs["north"];
37226         var south = rs["south"]; 
37227         var west = rs["west"];
37228         var east = rs["east"];
37229         var center = rs["center"];
37230         //if(this.hideOnLayout){ // not supported anymore
37231             //c.el.setStyle("display", "none");
37232         //}
37233         if(north && north.isVisible()){
37234             var b = north.getBox();
37235             var m = north.getMargins();
37236             b.width = w - (m.left+m.right);
37237             b.x = m.left;
37238             b.y = m.top;
37239             centerY = b.height + b.y + m.bottom;
37240             centerH -= centerY;
37241             north.updateBox(this.safeBox(b));
37242         }
37243         if(south && south.isVisible()){
37244             var b = south.getBox();
37245             var m = south.getMargins();
37246             b.width = w - (m.left+m.right);
37247             b.x = m.left;
37248             var totalHeight = (b.height + m.top + m.bottom);
37249             b.y = h - totalHeight + m.top;
37250             centerH -= totalHeight;
37251             south.updateBox(this.safeBox(b));
37252         }
37253         if(west && west.isVisible()){
37254             var b = west.getBox();
37255             var m = west.getMargins();
37256             b.height = centerH - (m.top+m.bottom);
37257             b.x = m.left;
37258             b.y = centerY + m.top;
37259             var totalWidth = (b.width + m.left + m.right);
37260             centerX += totalWidth;
37261             centerW -= totalWidth;
37262             west.updateBox(this.safeBox(b));
37263         }
37264         if(east && east.isVisible()){
37265             var b = east.getBox();
37266             var m = east.getMargins();
37267             b.height = centerH - (m.top+m.bottom);
37268             var totalWidth = (b.width + m.left + m.right);
37269             b.x = w - totalWidth + m.left;
37270             b.y = centerY + m.top;
37271             centerW -= totalWidth;
37272             east.updateBox(this.safeBox(b));
37273         }
37274         if(center){
37275             var m = center.getMargins();
37276             var centerBox = {
37277                 x: centerX + m.left,
37278                 y: centerY + m.top,
37279                 width: centerW - (m.left+m.right),
37280                 height: centerH - (m.top+m.bottom)
37281             };
37282             //if(this.hideOnLayout){
37283                 //center.el.setStyle("display", "block");
37284             //}
37285             center.updateBox(this.safeBox(centerBox));
37286         }
37287         this.el.repaint();
37288         this.fireEvent("layout", this);
37289     },
37290
37291     // private
37292     safeBox : function(box){
37293         box.width = Math.max(0, box.width);
37294         box.height = Math.max(0, box.height);
37295         return box;
37296     },
37297
37298     /**
37299      * Adds a ContentPanel (or subclass) to this layout.
37300      * @param {String} target The target region key (north, south, east, west or center).
37301      * @param {Roo.ContentPanel} panel The panel to add
37302      * @return {Roo.ContentPanel} The added panel
37303      */
37304     add : function(target, panel){
37305          
37306         target = target.toLowerCase();
37307         return this.regions[target].add(panel);
37308     },
37309
37310     /**
37311      * Remove a ContentPanel (or subclass) to this layout.
37312      * @param {String} target The target region key (north, south, east, west or center).
37313      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37314      * @return {Roo.ContentPanel} The removed panel
37315      */
37316     remove : function(target, panel){
37317         target = target.toLowerCase();
37318         return this.regions[target].remove(panel);
37319     },
37320
37321     /**
37322      * Searches all regions for a panel with the specified id
37323      * @param {String} panelId
37324      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37325      */
37326     findPanel : function(panelId){
37327         var rs = this.regions;
37328         for(var target in rs){
37329             if(typeof rs[target] != "function"){
37330                 var p = rs[target].getPanel(panelId);
37331                 if(p){
37332                     return p;
37333                 }
37334             }
37335         }
37336         return null;
37337     },
37338
37339     /**
37340      * Searches all regions for a panel with the specified id and activates (shows) it.
37341      * @param {String/ContentPanel} panelId The panels id or the panel itself
37342      * @return {Roo.ContentPanel} The shown panel or null
37343      */
37344     showPanel : function(panelId) {
37345       var rs = this.regions;
37346       for(var target in rs){
37347          var r = rs[target];
37348          if(typeof r != "function"){
37349             if(r.hasPanel(panelId)){
37350                return r.showPanel(panelId);
37351             }
37352          }
37353       }
37354       return null;
37355    },
37356
37357    /**
37358      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37359      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37360      */
37361    /*
37362     restoreState : function(provider){
37363         if(!provider){
37364             provider = Roo.state.Manager;
37365         }
37366         var sm = new Roo.LayoutStateManager();
37367         sm.init(this, provider);
37368     },
37369 */
37370  
37371  
37372     /**
37373      * Adds a xtype elements to the layout.
37374      * <pre><code>
37375
37376 layout.addxtype({
37377        xtype : 'ContentPanel',
37378        region: 'west',
37379        items: [ .... ]
37380    }
37381 );
37382
37383 layout.addxtype({
37384         xtype : 'NestedLayoutPanel',
37385         region: 'west',
37386         layout: {
37387            center: { },
37388            west: { }   
37389         },
37390         items : [ ... list of content panels or nested layout panels.. ]
37391    }
37392 );
37393 </code></pre>
37394      * @param {Object} cfg Xtype definition of item to add.
37395      */
37396     addxtype : function(cfg)
37397     {
37398         // basically accepts a pannel...
37399         // can accept a layout region..!?!?
37400         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37401         
37402         
37403         // theory?  children can only be panels??
37404         
37405         //if (!cfg.xtype.match(/Panel$/)) {
37406         //    return false;
37407         //}
37408         var ret = false;
37409         
37410         if (typeof(cfg.region) == 'undefined') {
37411             Roo.log("Failed to add Panel, region was not set");
37412             Roo.log(cfg);
37413             return false;
37414         }
37415         var region = cfg.region;
37416         delete cfg.region;
37417         
37418           
37419         var xitems = [];
37420         if (cfg.items) {
37421             xitems = cfg.items;
37422             delete cfg.items;
37423         }
37424         var nb = false;
37425         
37426         if ( region == 'center') {
37427             Roo.log("Center: " + cfg.title);
37428         }
37429         
37430         
37431         switch(cfg.xtype) 
37432         {
37433             case 'Content':  // ContentPanel (el, cfg)
37434             case 'Scroll':  // ContentPanel (el, cfg)
37435             case 'View': 
37436                 cfg.autoCreate = cfg.autoCreate || true;
37437                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37438                 //} else {
37439                 //    var el = this.el.createChild();
37440                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37441                 //}
37442                 
37443                 this.add(region, ret);
37444                 break;
37445             
37446             /*
37447             case 'TreePanel': // our new panel!
37448                 cfg.el = this.el.createChild();
37449                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37450                 this.add(region, ret);
37451                 break;
37452             */
37453             
37454             case 'Nest': 
37455                 // create a new Layout (which is  a Border Layout...
37456                 
37457                 var clayout = cfg.layout;
37458                 clayout.el  = this.el.createChild();
37459                 clayout.items   = clayout.items  || [];
37460                 
37461                 delete cfg.layout;
37462                 
37463                 // replace this exitems with the clayout ones..
37464                 xitems = clayout.items;
37465                  
37466                 // force background off if it's in center...
37467                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37468                     cfg.background = false;
37469                 }
37470                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37471                 
37472                 
37473                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37474                 //console.log('adding nested layout panel '  + cfg.toSource());
37475                 this.add(region, ret);
37476                 nb = {}; /// find first...
37477                 break;
37478             
37479             case 'Grid':
37480                 
37481                 // needs grid and region
37482                 
37483                 //var el = this.getRegion(region).el.createChild();
37484                 /*
37485                  *var el = this.el.createChild();
37486                 // create the grid first...
37487                 cfg.grid.container = el;
37488                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37489                 */
37490                 
37491                 if (region == 'center' && this.active ) {
37492                     cfg.background = false;
37493                 }
37494                 
37495                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37496                 
37497                 this.add(region, ret);
37498                 /*
37499                 if (cfg.background) {
37500                     // render grid on panel activation (if panel background)
37501                     ret.on('activate', function(gp) {
37502                         if (!gp.grid.rendered) {
37503                     //        gp.grid.render(el);
37504                         }
37505                     });
37506                 } else {
37507                   //  cfg.grid.render(el);
37508                 }
37509                 */
37510                 break;
37511            
37512            
37513             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37514                 // it was the old xcomponent building that caused this before.
37515                 // espeically if border is the top element in the tree.
37516                 ret = this;
37517                 break; 
37518                 
37519                     
37520                 
37521                 
37522                 
37523             default:
37524                 /*
37525                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37526                     
37527                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37528                     this.add(region, ret);
37529                 } else {
37530                 */
37531                     Roo.log(cfg);
37532                     throw "Can not add '" + cfg.xtype + "' to Border";
37533                     return null;
37534              
37535                                 
37536              
37537         }
37538         this.beginUpdate();
37539         // add children..
37540         var region = '';
37541         var abn = {};
37542         Roo.each(xitems, function(i)  {
37543             region = nb && i.region ? i.region : false;
37544             
37545             var add = ret.addxtype(i);
37546            
37547             if (region) {
37548                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37549                 if (!i.background) {
37550                     abn[region] = nb[region] ;
37551                 }
37552             }
37553             
37554         });
37555         this.endUpdate();
37556
37557         // make the last non-background panel active..
37558         //if (nb) { Roo.log(abn); }
37559         if (nb) {
37560             
37561             for(var r in abn) {
37562                 region = this.getRegion(r);
37563                 if (region) {
37564                     // tried using nb[r], but it does not work..
37565                      
37566                     region.showPanel(abn[r]);
37567                    
37568                 }
37569             }
37570         }
37571         return ret;
37572         
37573     },
37574     
37575     
37576 // private
37577     factory : function(cfg)
37578     {
37579         
37580         var validRegions = Roo.bootstrap.layout.Border.regions;
37581
37582         var target = cfg.region;
37583         cfg.mgr = this;
37584         
37585         var r = Roo.bootstrap.layout;
37586         Roo.log(target);
37587         switch(target){
37588             case "north":
37589                 return new r.North(cfg);
37590             case "south":
37591                 return new r.South(cfg);
37592             case "east":
37593                 return new r.East(cfg);
37594             case "west":
37595                 return new r.West(cfg);
37596             case "center":
37597                 return new r.Center(cfg);
37598         }
37599         throw 'Layout region "'+target+'" not supported.';
37600     }
37601     
37602     
37603 });
37604  /*
37605  * Based on:
37606  * Ext JS Library 1.1.1
37607  * Copyright(c) 2006-2007, Ext JS, LLC.
37608  *
37609  * Originally Released Under LGPL - original licence link has changed is not relivant.
37610  *
37611  * Fork - LGPL
37612  * <script type="text/javascript">
37613  */
37614  
37615 /**
37616  * @class Roo.bootstrap.layout.Basic
37617  * @extends Roo.util.Observable
37618  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37619  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37620  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37621  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37622  * @cfg {string}   region  the region that it inhabits..
37623  * @cfg {bool}   skipConfig skip config?
37624  * 
37625
37626  */
37627 Roo.bootstrap.layout.Basic = function(config){
37628     
37629     this.mgr = config.mgr;
37630     
37631     this.position = config.region;
37632     
37633     var skipConfig = config.skipConfig;
37634     
37635     this.events = {
37636         /**
37637          * @scope Roo.BasicLayoutRegion
37638          */
37639         
37640         /**
37641          * @event beforeremove
37642          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37643          * @param {Roo.LayoutRegion} this
37644          * @param {Roo.ContentPanel} panel The panel
37645          * @param {Object} e The cancel event object
37646          */
37647         "beforeremove" : true,
37648         /**
37649          * @event invalidated
37650          * Fires when the layout for this region is changed.
37651          * @param {Roo.LayoutRegion} this
37652          */
37653         "invalidated" : true,
37654         /**
37655          * @event visibilitychange
37656          * Fires when this region is shown or hidden 
37657          * @param {Roo.LayoutRegion} this
37658          * @param {Boolean} visibility true or false
37659          */
37660         "visibilitychange" : true,
37661         /**
37662          * @event paneladded
37663          * Fires when a panel is added. 
37664          * @param {Roo.LayoutRegion} this
37665          * @param {Roo.ContentPanel} panel The panel
37666          */
37667         "paneladded" : true,
37668         /**
37669          * @event panelremoved
37670          * Fires when a panel is removed. 
37671          * @param {Roo.LayoutRegion} this
37672          * @param {Roo.ContentPanel} panel The panel
37673          */
37674         "panelremoved" : true,
37675         /**
37676          * @event beforecollapse
37677          * Fires when this region before collapse.
37678          * @param {Roo.LayoutRegion} this
37679          */
37680         "beforecollapse" : true,
37681         /**
37682          * @event collapsed
37683          * Fires when this region is collapsed.
37684          * @param {Roo.LayoutRegion} this
37685          */
37686         "collapsed" : true,
37687         /**
37688          * @event expanded
37689          * Fires when this region is expanded.
37690          * @param {Roo.LayoutRegion} this
37691          */
37692         "expanded" : true,
37693         /**
37694          * @event slideshow
37695          * Fires when this region is slid into view.
37696          * @param {Roo.LayoutRegion} this
37697          */
37698         "slideshow" : true,
37699         /**
37700          * @event slidehide
37701          * Fires when this region slides out of view. 
37702          * @param {Roo.LayoutRegion} this
37703          */
37704         "slidehide" : true,
37705         /**
37706          * @event panelactivated
37707          * Fires when a panel is activated. 
37708          * @param {Roo.LayoutRegion} this
37709          * @param {Roo.ContentPanel} panel The activated panel
37710          */
37711         "panelactivated" : true,
37712         /**
37713          * @event resized
37714          * Fires when the user resizes this region. 
37715          * @param {Roo.LayoutRegion} this
37716          * @param {Number} newSize The new size (width for east/west, height for north/south)
37717          */
37718         "resized" : true
37719     };
37720     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37721     this.panels = new Roo.util.MixedCollection();
37722     this.panels.getKey = this.getPanelId.createDelegate(this);
37723     this.box = null;
37724     this.activePanel = null;
37725     // ensure listeners are added...
37726     
37727     if (config.listeners || config.events) {
37728         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37729             listeners : config.listeners || {},
37730             events : config.events || {}
37731         });
37732     }
37733     
37734     if(skipConfig !== true){
37735         this.applyConfig(config);
37736     }
37737 };
37738
37739 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37740 {
37741     getPanelId : function(p){
37742         return p.getId();
37743     },
37744     
37745     applyConfig : function(config){
37746         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37747         this.config = config;
37748         
37749     },
37750     
37751     /**
37752      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37753      * the width, for horizontal (north, south) the height.
37754      * @param {Number} newSize The new width or height
37755      */
37756     resizeTo : function(newSize){
37757         var el = this.el ? this.el :
37758                  (this.activePanel ? this.activePanel.getEl() : null);
37759         if(el){
37760             switch(this.position){
37761                 case "east":
37762                 case "west":
37763                     el.setWidth(newSize);
37764                     this.fireEvent("resized", this, newSize);
37765                 break;
37766                 case "north":
37767                 case "south":
37768                     el.setHeight(newSize);
37769                     this.fireEvent("resized", this, newSize);
37770                 break;                
37771             }
37772         }
37773     },
37774     
37775     getBox : function(){
37776         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37777     },
37778     
37779     getMargins : function(){
37780         return this.margins;
37781     },
37782     
37783     updateBox : function(box){
37784         this.box = box;
37785         var el = this.activePanel.getEl();
37786         el.dom.style.left = box.x + "px";
37787         el.dom.style.top = box.y + "px";
37788         this.activePanel.setSize(box.width, box.height);
37789     },
37790     
37791     /**
37792      * Returns the container element for this region.
37793      * @return {Roo.Element}
37794      */
37795     getEl : function(){
37796         return this.activePanel;
37797     },
37798     
37799     /**
37800      * Returns true if this region is currently visible.
37801      * @return {Boolean}
37802      */
37803     isVisible : function(){
37804         return this.activePanel ? true : false;
37805     },
37806     
37807     setActivePanel : function(panel){
37808         panel = this.getPanel(panel);
37809         if(this.activePanel && this.activePanel != panel){
37810             this.activePanel.setActiveState(false);
37811             this.activePanel.getEl().setLeftTop(-10000,-10000);
37812         }
37813         this.activePanel = panel;
37814         panel.setActiveState(true);
37815         if(this.box){
37816             panel.setSize(this.box.width, this.box.height);
37817         }
37818         this.fireEvent("panelactivated", this, panel);
37819         this.fireEvent("invalidated");
37820     },
37821     
37822     /**
37823      * Show the specified panel.
37824      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37825      * @return {Roo.ContentPanel} The shown panel or null
37826      */
37827     showPanel : function(panel){
37828         panel = this.getPanel(panel);
37829         if(panel){
37830             this.setActivePanel(panel);
37831         }
37832         return panel;
37833     },
37834     
37835     /**
37836      * Get the active panel for this region.
37837      * @return {Roo.ContentPanel} The active panel or null
37838      */
37839     getActivePanel : function(){
37840         return this.activePanel;
37841     },
37842     
37843     /**
37844      * Add the passed ContentPanel(s)
37845      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37846      * @return {Roo.ContentPanel} The panel added (if only one was added)
37847      */
37848     add : function(panel){
37849         if(arguments.length > 1){
37850             for(var i = 0, len = arguments.length; i < len; i++) {
37851                 this.add(arguments[i]);
37852             }
37853             return null;
37854         }
37855         if(this.hasPanel(panel)){
37856             this.showPanel(panel);
37857             return panel;
37858         }
37859         var el = panel.getEl();
37860         if(el.dom.parentNode != this.mgr.el.dom){
37861             this.mgr.el.dom.appendChild(el.dom);
37862         }
37863         if(panel.setRegion){
37864             panel.setRegion(this);
37865         }
37866         this.panels.add(panel);
37867         el.setStyle("position", "absolute");
37868         if(!panel.background){
37869             this.setActivePanel(panel);
37870             if(this.config.initialSize && this.panels.getCount()==1){
37871                 this.resizeTo(this.config.initialSize);
37872             }
37873         }
37874         this.fireEvent("paneladded", this, panel);
37875         return panel;
37876     },
37877     
37878     /**
37879      * Returns true if the panel is in this region.
37880      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37881      * @return {Boolean}
37882      */
37883     hasPanel : function(panel){
37884         if(typeof panel == "object"){ // must be panel obj
37885             panel = panel.getId();
37886         }
37887         return this.getPanel(panel) ? true : false;
37888     },
37889     
37890     /**
37891      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37892      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37893      * @param {Boolean} preservePanel Overrides the config preservePanel option
37894      * @return {Roo.ContentPanel} The panel that was removed
37895      */
37896     remove : function(panel, preservePanel){
37897         panel = this.getPanel(panel);
37898         if(!panel){
37899             return null;
37900         }
37901         var e = {};
37902         this.fireEvent("beforeremove", this, panel, e);
37903         if(e.cancel === true){
37904             return null;
37905         }
37906         var panelId = panel.getId();
37907         this.panels.removeKey(panelId);
37908         return panel;
37909     },
37910     
37911     /**
37912      * Returns the panel specified or null if it's not in this region.
37913      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37914      * @return {Roo.ContentPanel}
37915      */
37916     getPanel : function(id){
37917         if(typeof id == "object"){ // must be panel obj
37918             return id;
37919         }
37920         return this.panels.get(id);
37921     },
37922     
37923     /**
37924      * Returns this regions position (north/south/east/west/center).
37925      * @return {String} 
37926      */
37927     getPosition: function(){
37928         return this.position;    
37929     }
37930 });/*
37931  * Based on:
37932  * Ext JS Library 1.1.1
37933  * Copyright(c) 2006-2007, Ext JS, LLC.
37934  *
37935  * Originally Released Under LGPL - original licence link has changed is not relivant.
37936  *
37937  * Fork - LGPL
37938  * <script type="text/javascript">
37939  */
37940  
37941 /**
37942  * @class Roo.bootstrap.layout.Region
37943  * @extends Roo.bootstrap.layout.Basic
37944  * This class represents a region in a layout manager.
37945  
37946  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37947  * @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})
37948  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37949  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37950  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37951  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37952  * @cfg {String}    title           The title for the region (overrides panel titles)
37953  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37954  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37955  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37956  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37957  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37958  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37959  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37960  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37961  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37962  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37963
37964  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37965  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37966  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37967  * @cfg {Number}    width           For East/West panels
37968  * @cfg {Number}    height          For North/South panels
37969  * @cfg {Boolean}   split           To show the splitter
37970  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37971  * 
37972  * @cfg {string}   cls             Extra CSS classes to add to region
37973  * 
37974  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37975  * @cfg {string}   region  the region that it inhabits..
37976  *
37977
37978  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37979  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37980
37981  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37982  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37983  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37984  */
37985 Roo.bootstrap.layout.Region = function(config)
37986 {
37987     this.applyConfig(config);
37988
37989     var mgr = config.mgr;
37990     var pos = config.region;
37991     config.skipConfig = true;
37992     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37993     
37994     if (mgr.el) {
37995         this.onRender(mgr.el);   
37996     }
37997      
37998     this.visible = true;
37999     this.collapsed = false;
38000     this.unrendered_panels = [];
38001 };
38002
38003 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38004
38005     position: '', // set by wrapper (eg. north/south etc..)
38006     unrendered_panels : null,  // unrendered panels.
38007     
38008     tabPosition : false,
38009     
38010     mgr: false, // points to 'Border'
38011     
38012     
38013     createBody : function(){
38014         /** This region's body element 
38015         * @type Roo.Element */
38016         this.bodyEl = this.el.createChild({
38017                 tag: "div",
38018                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38019         });
38020     },
38021
38022     onRender: function(ctr, pos)
38023     {
38024         var dh = Roo.DomHelper;
38025         /** This region's container element 
38026         * @type Roo.Element */
38027         this.el = dh.append(ctr.dom, {
38028                 tag: "div",
38029                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38030             }, true);
38031         /** This region's title element 
38032         * @type Roo.Element */
38033     
38034         this.titleEl = dh.append(this.el.dom,  {
38035                 tag: "div",
38036                 unselectable: "on",
38037                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38038                 children:[
38039                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38040                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38041                 ]
38042             }, true);
38043         
38044         this.titleEl.enableDisplayMode();
38045         /** This region's title text element 
38046         * @type HTMLElement */
38047         this.titleTextEl = this.titleEl.dom.firstChild;
38048         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38049         /*
38050         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38051         this.closeBtn.enableDisplayMode();
38052         this.closeBtn.on("click", this.closeClicked, this);
38053         this.closeBtn.hide();
38054     */
38055         this.createBody(this.config);
38056         if(this.config.hideWhenEmpty){
38057             this.hide();
38058             this.on("paneladded", this.validateVisibility, this);
38059             this.on("panelremoved", this.validateVisibility, this);
38060         }
38061         if(this.autoScroll){
38062             this.bodyEl.setStyle("overflow", "auto");
38063         }else{
38064             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38065         }
38066         //if(c.titlebar !== false){
38067             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38068                 this.titleEl.hide();
38069             }else{
38070                 this.titleEl.show();
38071                 if(this.config.title){
38072                     this.titleTextEl.innerHTML = this.config.title;
38073                 }
38074             }
38075         //}
38076         if(this.config.collapsed){
38077             this.collapse(true);
38078         }
38079         if(this.config.hidden){
38080             this.hide();
38081         }
38082         
38083         if (this.unrendered_panels && this.unrendered_panels.length) {
38084             for (var i =0;i< this.unrendered_panels.length; i++) {
38085                 this.add(this.unrendered_panels[i]);
38086             }
38087             this.unrendered_panels = null;
38088             
38089         }
38090         
38091     },
38092     
38093     applyConfig : function(c)
38094     {
38095         /*
38096          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38097             var dh = Roo.DomHelper;
38098             if(c.titlebar !== false){
38099                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38100                 this.collapseBtn.on("click", this.collapse, this);
38101                 this.collapseBtn.enableDisplayMode();
38102                 /*
38103                 if(c.showPin === true || this.showPin){
38104                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38105                     this.stickBtn.enableDisplayMode();
38106                     this.stickBtn.on("click", this.expand, this);
38107                     this.stickBtn.hide();
38108                 }
38109                 
38110             }
38111             */
38112             /** This region's collapsed element
38113             * @type Roo.Element */
38114             /*
38115              *
38116             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38117                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38118             ]}, true);
38119             
38120             if(c.floatable !== false){
38121                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38122                this.collapsedEl.on("click", this.collapseClick, this);
38123             }
38124
38125             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38126                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38127                    id: "message", unselectable: "on", style:{"float":"left"}});
38128                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38129              }
38130             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38131             this.expandBtn.on("click", this.expand, this);
38132             
38133         }
38134         
38135         if(this.collapseBtn){
38136             this.collapseBtn.setVisible(c.collapsible == true);
38137         }
38138         
38139         this.cmargins = c.cmargins || this.cmargins ||
38140                          (this.position == "west" || this.position == "east" ?
38141                              {top: 0, left: 2, right:2, bottom: 0} :
38142                              {top: 2, left: 0, right:0, bottom: 2});
38143         */
38144         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38145         
38146         
38147         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38148         
38149         this.autoScroll = c.autoScroll || false;
38150         
38151         
38152        
38153         
38154         this.duration = c.duration || .30;
38155         this.slideDuration = c.slideDuration || .45;
38156         this.config = c;
38157        
38158     },
38159     /**
38160      * Returns true if this region is currently visible.
38161      * @return {Boolean}
38162      */
38163     isVisible : function(){
38164         return this.visible;
38165     },
38166
38167     /**
38168      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38169      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38170      */
38171     //setCollapsedTitle : function(title){
38172     //    title = title || "&#160;";
38173      //   if(this.collapsedTitleTextEl){
38174       //      this.collapsedTitleTextEl.innerHTML = title;
38175        // }
38176     //},
38177
38178     getBox : function(){
38179         var b;
38180       //  if(!this.collapsed){
38181             b = this.el.getBox(false, true);
38182        // }else{
38183           //  b = this.collapsedEl.getBox(false, true);
38184         //}
38185         return b;
38186     },
38187
38188     getMargins : function(){
38189         return this.margins;
38190         //return this.collapsed ? this.cmargins : this.margins;
38191     },
38192 /*
38193     highlight : function(){
38194         this.el.addClass("x-layout-panel-dragover");
38195     },
38196
38197     unhighlight : function(){
38198         this.el.removeClass("x-layout-panel-dragover");
38199     },
38200 */
38201     updateBox : function(box)
38202     {
38203         if (!this.bodyEl) {
38204             return; // not rendered yet..
38205         }
38206         
38207         this.box = box;
38208         if(!this.collapsed){
38209             this.el.dom.style.left = box.x + "px";
38210             this.el.dom.style.top = box.y + "px";
38211             this.updateBody(box.width, box.height);
38212         }else{
38213             this.collapsedEl.dom.style.left = box.x + "px";
38214             this.collapsedEl.dom.style.top = box.y + "px";
38215             this.collapsedEl.setSize(box.width, box.height);
38216         }
38217         if(this.tabs){
38218             this.tabs.autoSizeTabs();
38219         }
38220     },
38221
38222     updateBody : function(w, h)
38223     {
38224         if(w !== null){
38225             this.el.setWidth(w);
38226             w -= this.el.getBorderWidth("rl");
38227             if(this.config.adjustments){
38228                 w += this.config.adjustments[0];
38229             }
38230         }
38231         if(h !== null && h > 0){
38232             this.el.setHeight(h);
38233             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38234             h -= this.el.getBorderWidth("tb");
38235             if(this.config.adjustments){
38236                 h += this.config.adjustments[1];
38237             }
38238             this.bodyEl.setHeight(h);
38239             if(this.tabs){
38240                 h = this.tabs.syncHeight(h);
38241             }
38242         }
38243         if(this.panelSize){
38244             w = w !== null ? w : this.panelSize.width;
38245             h = h !== null ? h : this.panelSize.height;
38246         }
38247         if(this.activePanel){
38248             var el = this.activePanel.getEl();
38249             w = w !== null ? w : el.getWidth();
38250             h = h !== null ? h : el.getHeight();
38251             this.panelSize = {width: w, height: h};
38252             this.activePanel.setSize(w, h);
38253         }
38254         if(Roo.isIE && this.tabs){
38255             this.tabs.el.repaint();
38256         }
38257     },
38258
38259     /**
38260      * Returns the container element for this region.
38261      * @return {Roo.Element}
38262      */
38263     getEl : function(){
38264         return this.el;
38265     },
38266
38267     /**
38268      * Hides this region.
38269      */
38270     hide : function(){
38271         //if(!this.collapsed){
38272             this.el.dom.style.left = "-2000px";
38273             this.el.hide();
38274         //}else{
38275          //   this.collapsedEl.dom.style.left = "-2000px";
38276          //   this.collapsedEl.hide();
38277        // }
38278         this.visible = false;
38279         this.fireEvent("visibilitychange", this, false);
38280     },
38281
38282     /**
38283      * Shows this region if it was previously hidden.
38284      */
38285     show : function(){
38286         //if(!this.collapsed){
38287             this.el.show();
38288         //}else{
38289         //    this.collapsedEl.show();
38290        // }
38291         this.visible = true;
38292         this.fireEvent("visibilitychange", this, true);
38293     },
38294 /*
38295     closeClicked : function(){
38296         if(this.activePanel){
38297             this.remove(this.activePanel);
38298         }
38299     },
38300
38301     collapseClick : function(e){
38302         if(this.isSlid){
38303            e.stopPropagation();
38304            this.slideIn();
38305         }else{
38306            e.stopPropagation();
38307            this.slideOut();
38308         }
38309     },
38310 */
38311     /**
38312      * Collapses this region.
38313      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38314      */
38315     /*
38316     collapse : function(skipAnim, skipCheck = false){
38317         if(this.collapsed) {
38318             return;
38319         }
38320         
38321         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38322             
38323             this.collapsed = true;
38324             if(this.split){
38325                 this.split.el.hide();
38326             }
38327             if(this.config.animate && skipAnim !== true){
38328                 this.fireEvent("invalidated", this);
38329                 this.animateCollapse();
38330             }else{
38331                 this.el.setLocation(-20000,-20000);
38332                 this.el.hide();
38333                 this.collapsedEl.show();
38334                 this.fireEvent("collapsed", this);
38335                 this.fireEvent("invalidated", this);
38336             }
38337         }
38338         
38339     },
38340 */
38341     animateCollapse : function(){
38342         // overridden
38343     },
38344
38345     /**
38346      * Expands this region if it was previously collapsed.
38347      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38348      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38349      */
38350     /*
38351     expand : function(e, skipAnim){
38352         if(e) {
38353             e.stopPropagation();
38354         }
38355         if(!this.collapsed || this.el.hasActiveFx()) {
38356             return;
38357         }
38358         if(this.isSlid){
38359             this.afterSlideIn();
38360             skipAnim = true;
38361         }
38362         this.collapsed = false;
38363         if(this.config.animate && skipAnim !== true){
38364             this.animateExpand();
38365         }else{
38366             this.el.show();
38367             if(this.split){
38368                 this.split.el.show();
38369             }
38370             this.collapsedEl.setLocation(-2000,-2000);
38371             this.collapsedEl.hide();
38372             this.fireEvent("invalidated", this);
38373             this.fireEvent("expanded", this);
38374         }
38375     },
38376 */
38377     animateExpand : function(){
38378         // overridden
38379     },
38380
38381     initTabs : function()
38382     {
38383         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38384         
38385         var ts = new Roo.bootstrap.panel.Tabs({
38386             el: this.bodyEl.dom,
38387             region : this,
38388             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38389             disableTooltips: this.config.disableTabTips,
38390             toolbar : this.config.toolbar
38391         });
38392         
38393         if(this.config.hideTabs){
38394             ts.stripWrap.setDisplayed(false);
38395         }
38396         this.tabs = ts;
38397         ts.resizeTabs = this.config.resizeTabs === true;
38398         ts.minTabWidth = this.config.minTabWidth || 40;
38399         ts.maxTabWidth = this.config.maxTabWidth || 250;
38400         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38401         ts.monitorResize = false;
38402         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38403         ts.bodyEl.addClass('roo-layout-tabs-body');
38404         this.panels.each(this.initPanelAsTab, this);
38405     },
38406
38407     initPanelAsTab : function(panel){
38408         var ti = this.tabs.addTab(
38409             panel.getEl().id,
38410             panel.getTitle(),
38411             null,
38412             this.config.closeOnTab && panel.isClosable(),
38413             panel.tpl
38414         );
38415         if(panel.tabTip !== undefined){
38416             ti.setTooltip(panel.tabTip);
38417         }
38418         ti.on("activate", function(){
38419               this.setActivePanel(panel);
38420         }, this);
38421         
38422         if(this.config.closeOnTab){
38423             ti.on("beforeclose", function(t, e){
38424                 e.cancel = true;
38425                 this.remove(panel);
38426             }, this);
38427         }
38428         
38429         panel.tabItem = ti;
38430         
38431         return ti;
38432     },
38433
38434     updatePanelTitle : function(panel, title)
38435     {
38436         if(this.activePanel == panel){
38437             this.updateTitle(title);
38438         }
38439         if(this.tabs){
38440             var ti = this.tabs.getTab(panel.getEl().id);
38441             ti.setText(title);
38442             if(panel.tabTip !== undefined){
38443                 ti.setTooltip(panel.tabTip);
38444             }
38445         }
38446     },
38447
38448     updateTitle : function(title){
38449         if(this.titleTextEl && !this.config.title){
38450             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38451         }
38452     },
38453
38454     setActivePanel : function(panel)
38455     {
38456         panel = this.getPanel(panel);
38457         if(this.activePanel && this.activePanel != panel){
38458             if(this.activePanel.setActiveState(false) === false){
38459                 return;
38460             }
38461         }
38462         this.activePanel = panel;
38463         panel.setActiveState(true);
38464         if(this.panelSize){
38465             panel.setSize(this.panelSize.width, this.panelSize.height);
38466         }
38467         if(this.closeBtn){
38468             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38469         }
38470         this.updateTitle(panel.getTitle());
38471         if(this.tabs){
38472             this.fireEvent("invalidated", this);
38473         }
38474         this.fireEvent("panelactivated", this, panel);
38475     },
38476
38477     /**
38478      * Shows the specified panel.
38479      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38480      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38481      */
38482     showPanel : function(panel)
38483     {
38484         panel = this.getPanel(panel);
38485         if(panel){
38486             if(this.tabs){
38487                 var tab = this.tabs.getTab(panel.getEl().id);
38488                 if(tab.isHidden()){
38489                     this.tabs.unhideTab(tab.id);
38490                 }
38491                 tab.activate();
38492             }else{
38493                 this.setActivePanel(panel);
38494             }
38495         }
38496         return panel;
38497     },
38498
38499     /**
38500      * Get the active panel for this region.
38501      * @return {Roo.ContentPanel} The active panel or null
38502      */
38503     getActivePanel : function(){
38504         return this.activePanel;
38505     },
38506
38507     validateVisibility : function(){
38508         if(this.panels.getCount() < 1){
38509             this.updateTitle("&#160;");
38510             this.closeBtn.hide();
38511             this.hide();
38512         }else{
38513             if(!this.isVisible()){
38514                 this.show();
38515             }
38516         }
38517     },
38518
38519     /**
38520      * Adds the passed ContentPanel(s) to this region.
38521      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38522      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38523      */
38524     add : function(panel)
38525     {
38526         if(arguments.length > 1){
38527             for(var i = 0, len = arguments.length; i < len; i++) {
38528                 this.add(arguments[i]);
38529             }
38530             return null;
38531         }
38532         
38533         // if we have not been rendered yet, then we can not really do much of this..
38534         if (!this.bodyEl) {
38535             this.unrendered_panels.push(panel);
38536             return panel;
38537         }
38538         
38539         
38540         
38541         
38542         if(this.hasPanel(panel)){
38543             this.showPanel(panel);
38544             return panel;
38545         }
38546         panel.setRegion(this);
38547         this.panels.add(panel);
38548        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38549             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38550             // and hide them... ???
38551             this.bodyEl.dom.appendChild(panel.getEl().dom);
38552             if(panel.background !== true){
38553                 this.setActivePanel(panel);
38554             }
38555             this.fireEvent("paneladded", this, panel);
38556             return panel;
38557         }
38558         */
38559         if(!this.tabs){
38560             this.initTabs();
38561         }else{
38562             this.initPanelAsTab(panel);
38563         }
38564         
38565         
38566         if(panel.background !== true){
38567             this.tabs.activate(panel.getEl().id);
38568         }
38569         this.fireEvent("paneladded", this, panel);
38570         return panel;
38571     },
38572
38573     /**
38574      * Hides the tab for the specified panel.
38575      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38576      */
38577     hidePanel : function(panel){
38578         if(this.tabs && (panel = this.getPanel(panel))){
38579             this.tabs.hideTab(panel.getEl().id);
38580         }
38581     },
38582
38583     /**
38584      * Unhides the tab for a previously hidden panel.
38585      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38586      */
38587     unhidePanel : function(panel){
38588         if(this.tabs && (panel = this.getPanel(panel))){
38589             this.tabs.unhideTab(panel.getEl().id);
38590         }
38591     },
38592
38593     clearPanels : function(){
38594         while(this.panels.getCount() > 0){
38595              this.remove(this.panels.first());
38596         }
38597     },
38598
38599     /**
38600      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38601      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38602      * @param {Boolean} preservePanel Overrides the config preservePanel option
38603      * @return {Roo.ContentPanel} The panel that was removed
38604      */
38605     remove : function(panel, preservePanel)
38606     {
38607         panel = this.getPanel(panel);
38608         if(!panel){
38609             return null;
38610         }
38611         var e = {};
38612         this.fireEvent("beforeremove", this, panel, e);
38613         if(e.cancel === true){
38614             return null;
38615         }
38616         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38617         var panelId = panel.getId();
38618         this.panels.removeKey(panelId);
38619         if(preservePanel){
38620             document.body.appendChild(panel.getEl().dom);
38621         }
38622         if(this.tabs){
38623             this.tabs.removeTab(panel.getEl().id);
38624         }else if (!preservePanel){
38625             this.bodyEl.dom.removeChild(panel.getEl().dom);
38626         }
38627         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38628             var p = this.panels.first();
38629             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38630             tempEl.appendChild(p.getEl().dom);
38631             this.bodyEl.update("");
38632             this.bodyEl.dom.appendChild(p.getEl().dom);
38633             tempEl = null;
38634             this.updateTitle(p.getTitle());
38635             this.tabs = null;
38636             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38637             this.setActivePanel(p);
38638         }
38639         panel.setRegion(null);
38640         if(this.activePanel == panel){
38641             this.activePanel = null;
38642         }
38643         if(this.config.autoDestroy !== false && preservePanel !== true){
38644             try{panel.destroy();}catch(e){}
38645         }
38646         this.fireEvent("panelremoved", this, panel);
38647         return panel;
38648     },
38649
38650     /**
38651      * Returns the TabPanel component used by this region
38652      * @return {Roo.TabPanel}
38653      */
38654     getTabs : function(){
38655         return this.tabs;
38656     },
38657
38658     createTool : function(parentEl, className){
38659         var btn = Roo.DomHelper.append(parentEl, {
38660             tag: "div",
38661             cls: "x-layout-tools-button",
38662             children: [ {
38663                 tag: "div",
38664                 cls: "roo-layout-tools-button-inner " + className,
38665                 html: "&#160;"
38666             }]
38667         }, true);
38668         btn.addClassOnOver("roo-layout-tools-button-over");
38669         return btn;
38670     }
38671 });/*
38672  * Based on:
38673  * Ext JS Library 1.1.1
38674  * Copyright(c) 2006-2007, Ext JS, LLC.
38675  *
38676  * Originally Released Under LGPL - original licence link has changed is not relivant.
38677  *
38678  * Fork - LGPL
38679  * <script type="text/javascript">
38680  */
38681  
38682
38683
38684 /**
38685  * @class Roo.SplitLayoutRegion
38686  * @extends Roo.LayoutRegion
38687  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38688  */
38689 Roo.bootstrap.layout.Split = function(config){
38690     this.cursor = config.cursor;
38691     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38692 };
38693
38694 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38695 {
38696     splitTip : "Drag to resize.",
38697     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38698     useSplitTips : false,
38699
38700     applyConfig : function(config){
38701         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38702     },
38703     
38704     onRender : function(ctr,pos) {
38705         
38706         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38707         if(!this.config.split){
38708             return;
38709         }
38710         if(!this.split){
38711             
38712             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38713                             tag: "div",
38714                             id: this.el.id + "-split",
38715                             cls: "roo-layout-split roo-layout-split-"+this.position,
38716                             html: "&#160;"
38717             });
38718             /** The SplitBar for this region 
38719             * @type Roo.SplitBar */
38720             // does not exist yet...
38721             Roo.log([this.position, this.orientation]);
38722             
38723             this.split = new Roo.bootstrap.SplitBar({
38724                 dragElement : splitEl,
38725                 resizingElement: this.el,
38726                 orientation : this.orientation
38727             });
38728             
38729             this.split.on("moved", this.onSplitMove, this);
38730             this.split.useShim = this.config.useShim === true;
38731             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38732             if(this.useSplitTips){
38733                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38734             }
38735             //if(config.collapsible){
38736             //    this.split.el.on("dblclick", this.collapse,  this);
38737             //}
38738         }
38739         if(typeof this.config.minSize != "undefined"){
38740             this.split.minSize = this.config.minSize;
38741         }
38742         if(typeof this.config.maxSize != "undefined"){
38743             this.split.maxSize = this.config.maxSize;
38744         }
38745         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38746             this.hideSplitter();
38747         }
38748         
38749     },
38750
38751     getHMaxSize : function(){
38752          var cmax = this.config.maxSize || 10000;
38753          var center = this.mgr.getRegion("center");
38754          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38755     },
38756
38757     getVMaxSize : function(){
38758          var cmax = this.config.maxSize || 10000;
38759          var center = this.mgr.getRegion("center");
38760          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38761     },
38762
38763     onSplitMove : function(split, newSize){
38764         this.fireEvent("resized", this, newSize);
38765     },
38766     
38767     /** 
38768      * Returns the {@link Roo.SplitBar} for this region.
38769      * @return {Roo.SplitBar}
38770      */
38771     getSplitBar : function(){
38772         return this.split;
38773     },
38774     
38775     hide : function(){
38776         this.hideSplitter();
38777         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38778     },
38779
38780     hideSplitter : function(){
38781         if(this.split){
38782             this.split.el.setLocation(-2000,-2000);
38783             this.split.el.hide();
38784         }
38785     },
38786
38787     show : function(){
38788         if(this.split){
38789             this.split.el.show();
38790         }
38791         Roo.bootstrap.layout.Split.superclass.show.call(this);
38792     },
38793     
38794     beforeSlide: function(){
38795         if(Roo.isGecko){// firefox overflow auto bug workaround
38796             this.bodyEl.clip();
38797             if(this.tabs) {
38798                 this.tabs.bodyEl.clip();
38799             }
38800             if(this.activePanel){
38801                 this.activePanel.getEl().clip();
38802                 
38803                 if(this.activePanel.beforeSlide){
38804                     this.activePanel.beforeSlide();
38805                 }
38806             }
38807         }
38808     },
38809     
38810     afterSlide : function(){
38811         if(Roo.isGecko){// firefox overflow auto bug workaround
38812             this.bodyEl.unclip();
38813             if(this.tabs) {
38814                 this.tabs.bodyEl.unclip();
38815             }
38816             if(this.activePanel){
38817                 this.activePanel.getEl().unclip();
38818                 if(this.activePanel.afterSlide){
38819                     this.activePanel.afterSlide();
38820                 }
38821             }
38822         }
38823     },
38824
38825     initAutoHide : function(){
38826         if(this.autoHide !== false){
38827             if(!this.autoHideHd){
38828                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38829                 this.autoHideHd = {
38830                     "mouseout": function(e){
38831                         if(!e.within(this.el, true)){
38832                             st.delay(500);
38833                         }
38834                     },
38835                     "mouseover" : function(e){
38836                         st.cancel();
38837                     },
38838                     scope : this
38839                 };
38840             }
38841             this.el.on(this.autoHideHd);
38842         }
38843     },
38844
38845     clearAutoHide : function(){
38846         if(this.autoHide !== false){
38847             this.el.un("mouseout", this.autoHideHd.mouseout);
38848             this.el.un("mouseover", this.autoHideHd.mouseover);
38849         }
38850     },
38851
38852     clearMonitor : function(){
38853         Roo.get(document).un("click", this.slideInIf, this);
38854     },
38855
38856     // these names are backwards but not changed for compat
38857     slideOut : function(){
38858         if(this.isSlid || this.el.hasActiveFx()){
38859             return;
38860         }
38861         this.isSlid = true;
38862         if(this.collapseBtn){
38863             this.collapseBtn.hide();
38864         }
38865         this.closeBtnState = this.closeBtn.getStyle('display');
38866         this.closeBtn.hide();
38867         if(this.stickBtn){
38868             this.stickBtn.show();
38869         }
38870         this.el.show();
38871         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38872         this.beforeSlide();
38873         this.el.setStyle("z-index", 10001);
38874         this.el.slideIn(this.getSlideAnchor(), {
38875             callback: function(){
38876                 this.afterSlide();
38877                 this.initAutoHide();
38878                 Roo.get(document).on("click", this.slideInIf, this);
38879                 this.fireEvent("slideshow", this);
38880             },
38881             scope: this,
38882             block: true
38883         });
38884     },
38885
38886     afterSlideIn : function(){
38887         this.clearAutoHide();
38888         this.isSlid = false;
38889         this.clearMonitor();
38890         this.el.setStyle("z-index", "");
38891         if(this.collapseBtn){
38892             this.collapseBtn.show();
38893         }
38894         this.closeBtn.setStyle('display', this.closeBtnState);
38895         if(this.stickBtn){
38896             this.stickBtn.hide();
38897         }
38898         this.fireEvent("slidehide", this);
38899     },
38900
38901     slideIn : function(cb){
38902         if(!this.isSlid || this.el.hasActiveFx()){
38903             Roo.callback(cb);
38904             return;
38905         }
38906         this.isSlid = false;
38907         this.beforeSlide();
38908         this.el.slideOut(this.getSlideAnchor(), {
38909             callback: function(){
38910                 this.el.setLeftTop(-10000, -10000);
38911                 this.afterSlide();
38912                 this.afterSlideIn();
38913                 Roo.callback(cb);
38914             },
38915             scope: this,
38916             block: true
38917         });
38918     },
38919     
38920     slideInIf : function(e){
38921         if(!e.within(this.el)){
38922             this.slideIn();
38923         }
38924     },
38925
38926     animateCollapse : function(){
38927         this.beforeSlide();
38928         this.el.setStyle("z-index", 20000);
38929         var anchor = this.getSlideAnchor();
38930         this.el.slideOut(anchor, {
38931             callback : function(){
38932                 this.el.setStyle("z-index", "");
38933                 this.collapsedEl.slideIn(anchor, {duration:.3});
38934                 this.afterSlide();
38935                 this.el.setLocation(-10000,-10000);
38936                 this.el.hide();
38937                 this.fireEvent("collapsed", this);
38938             },
38939             scope: this,
38940             block: true
38941         });
38942     },
38943
38944     animateExpand : function(){
38945         this.beforeSlide();
38946         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38947         this.el.setStyle("z-index", 20000);
38948         this.collapsedEl.hide({
38949             duration:.1
38950         });
38951         this.el.slideIn(this.getSlideAnchor(), {
38952             callback : function(){
38953                 this.el.setStyle("z-index", "");
38954                 this.afterSlide();
38955                 if(this.split){
38956                     this.split.el.show();
38957                 }
38958                 this.fireEvent("invalidated", this);
38959                 this.fireEvent("expanded", this);
38960             },
38961             scope: this,
38962             block: true
38963         });
38964     },
38965
38966     anchors : {
38967         "west" : "left",
38968         "east" : "right",
38969         "north" : "top",
38970         "south" : "bottom"
38971     },
38972
38973     sanchors : {
38974         "west" : "l",
38975         "east" : "r",
38976         "north" : "t",
38977         "south" : "b"
38978     },
38979
38980     canchors : {
38981         "west" : "tl-tr",
38982         "east" : "tr-tl",
38983         "north" : "tl-bl",
38984         "south" : "bl-tl"
38985     },
38986
38987     getAnchor : function(){
38988         return this.anchors[this.position];
38989     },
38990
38991     getCollapseAnchor : function(){
38992         return this.canchors[this.position];
38993     },
38994
38995     getSlideAnchor : function(){
38996         return this.sanchors[this.position];
38997     },
38998
38999     getAlignAdj : function(){
39000         var cm = this.cmargins;
39001         switch(this.position){
39002             case "west":
39003                 return [0, 0];
39004             break;
39005             case "east":
39006                 return [0, 0];
39007             break;
39008             case "north":
39009                 return [0, 0];
39010             break;
39011             case "south":
39012                 return [0, 0];
39013             break;
39014         }
39015     },
39016
39017     getExpandAdj : function(){
39018         var c = this.collapsedEl, cm = this.cmargins;
39019         switch(this.position){
39020             case "west":
39021                 return [-(cm.right+c.getWidth()+cm.left), 0];
39022             break;
39023             case "east":
39024                 return [cm.right+c.getWidth()+cm.left, 0];
39025             break;
39026             case "north":
39027                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39028             break;
39029             case "south":
39030                 return [0, cm.top+cm.bottom+c.getHeight()];
39031             break;
39032         }
39033     }
39034 });/*
39035  * Based on:
39036  * Ext JS Library 1.1.1
39037  * Copyright(c) 2006-2007, Ext JS, LLC.
39038  *
39039  * Originally Released Under LGPL - original licence link has changed is not relivant.
39040  *
39041  * Fork - LGPL
39042  * <script type="text/javascript">
39043  */
39044 /*
39045  * These classes are private internal classes
39046  */
39047 Roo.bootstrap.layout.Center = function(config){
39048     config.region = "center";
39049     Roo.bootstrap.layout.Region.call(this, config);
39050     this.visible = true;
39051     this.minWidth = config.minWidth || 20;
39052     this.minHeight = config.minHeight || 20;
39053 };
39054
39055 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39056     hide : function(){
39057         // center panel can't be hidden
39058     },
39059     
39060     show : function(){
39061         // center panel can't be hidden
39062     },
39063     
39064     getMinWidth: function(){
39065         return this.minWidth;
39066     },
39067     
39068     getMinHeight: function(){
39069         return this.minHeight;
39070     }
39071 });
39072
39073
39074
39075
39076  
39077
39078
39079
39080
39081
39082
39083 Roo.bootstrap.layout.North = function(config)
39084 {
39085     config.region = 'north';
39086     config.cursor = 'n-resize';
39087     
39088     Roo.bootstrap.layout.Split.call(this, config);
39089     
39090     
39091     if(this.split){
39092         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39093         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39094         this.split.el.addClass("roo-layout-split-v");
39095     }
39096     var size = config.initialSize || config.height;
39097     if(typeof size != "undefined"){
39098         this.el.setHeight(size);
39099     }
39100 };
39101 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39102 {
39103     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39104     
39105     
39106     
39107     getBox : function(){
39108         if(this.collapsed){
39109             return this.collapsedEl.getBox();
39110         }
39111         var box = this.el.getBox();
39112         if(this.split){
39113             box.height += this.split.el.getHeight();
39114         }
39115         return box;
39116     },
39117     
39118     updateBox : function(box){
39119         if(this.split && !this.collapsed){
39120             box.height -= this.split.el.getHeight();
39121             this.split.el.setLeft(box.x);
39122             this.split.el.setTop(box.y+box.height);
39123             this.split.el.setWidth(box.width);
39124         }
39125         if(this.collapsed){
39126             this.updateBody(box.width, null);
39127         }
39128         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39129     }
39130 });
39131
39132
39133
39134
39135
39136 Roo.bootstrap.layout.South = function(config){
39137     config.region = 'south';
39138     config.cursor = 's-resize';
39139     Roo.bootstrap.layout.Split.call(this, config);
39140     if(this.split){
39141         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39142         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39143         this.split.el.addClass("roo-layout-split-v");
39144     }
39145     var size = config.initialSize || config.height;
39146     if(typeof size != "undefined"){
39147         this.el.setHeight(size);
39148     }
39149 };
39150
39151 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39152     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39153     getBox : function(){
39154         if(this.collapsed){
39155             return this.collapsedEl.getBox();
39156         }
39157         var box = this.el.getBox();
39158         if(this.split){
39159             var sh = this.split.el.getHeight();
39160             box.height += sh;
39161             box.y -= sh;
39162         }
39163         return box;
39164     },
39165     
39166     updateBox : function(box){
39167         if(this.split && !this.collapsed){
39168             var sh = this.split.el.getHeight();
39169             box.height -= sh;
39170             box.y += sh;
39171             this.split.el.setLeft(box.x);
39172             this.split.el.setTop(box.y-sh);
39173             this.split.el.setWidth(box.width);
39174         }
39175         if(this.collapsed){
39176             this.updateBody(box.width, null);
39177         }
39178         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39179     }
39180 });
39181
39182 Roo.bootstrap.layout.East = function(config){
39183     config.region = "east";
39184     config.cursor = "e-resize";
39185     Roo.bootstrap.layout.Split.call(this, config);
39186     if(this.split){
39187         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39188         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39189         this.split.el.addClass("roo-layout-split-h");
39190     }
39191     var size = config.initialSize || config.width;
39192     if(typeof size != "undefined"){
39193         this.el.setWidth(size);
39194     }
39195 };
39196 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39197     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39198     getBox : function(){
39199         if(this.collapsed){
39200             return this.collapsedEl.getBox();
39201         }
39202         var box = this.el.getBox();
39203         if(this.split){
39204             var sw = this.split.el.getWidth();
39205             box.width += sw;
39206             box.x -= sw;
39207         }
39208         return box;
39209     },
39210
39211     updateBox : function(box){
39212         if(this.split && !this.collapsed){
39213             var sw = this.split.el.getWidth();
39214             box.width -= sw;
39215             this.split.el.setLeft(box.x);
39216             this.split.el.setTop(box.y);
39217             this.split.el.setHeight(box.height);
39218             box.x += sw;
39219         }
39220         if(this.collapsed){
39221             this.updateBody(null, box.height);
39222         }
39223         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39224     }
39225 });
39226
39227 Roo.bootstrap.layout.West = function(config){
39228     config.region = "west";
39229     config.cursor = "w-resize";
39230     
39231     Roo.bootstrap.layout.Split.call(this, config);
39232     if(this.split){
39233         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39234         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39235         this.split.el.addClass("roo-layout-split-h");
39236     }
39237     
39238 };
39239 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39240     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39241     
39242     onRender: function(ctr, pos)
39243     {
39244         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39245         var size = this.config.initialSize || this.config.width;
39246         if(typeof size != "undefined"){
39247             this.el.setWidth(size);
39248         }
39249     },
39250     
39251     getBox : function(){
39252         if(this.collapsed){
39253             return this.collapsedEl.getBox();
39254         }
39255         var box = this.el.getBox();
39256         if(this.split){
39257             box.width += this.split.el.getWidth();
39258         }
39259         return box;
39260     },
39261     
39262     updateBox : function(box){
39263         if(this.split && !this.collapsed){
39264             var sw = this.split.el.getWidth();
39265             box.width -= sw;
39266             this.split.el.setLeft(box.x+box.width);
39267             this.split.el.setTop(box.y);
39268             this.split.el.setHeight(box.height);
39269         }
39270         if(this.collapsed){
39271             this.updateBody(null, box.height);
39272         }
39273         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39274     }
39275 });Roo.namespace("Roo.bootstrap.panel");/*
39276  * Based on:
39277  * Ext JS Library 1.1.1
39278  * Copyright(c) 2006-2007, Ext JS, LLC.
39279  *
39280  * Originally Released Under LGPL - original licence link has changed is not relivant.
39281  *
39282  * Fork - LGPL
39283  * <script type="text/javascript">
39284  */
39285 /**
39286  * @class Roo.ContentPanel
39287  * @extends Roo.util.Observable
39288  * A basic ContentPanel element.
39289  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39290  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39291  * @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
39292  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39293  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39294  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39295  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39296  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39297  * @cfg {String} title          The title for this panel
39298  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39299  * @cfg {String} url            Calls {@link #setUrl} with this value
39300  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39301  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39302  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39303  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39304  * @cfg {Boolean} badges render the badges
39305  * @cfg {String} cls  extra classes to use  
39306  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39307
39308  * @constructor
39309  * Create a new ContentPanel.
39310  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39311  * @param {String/Object} config A string to set only the title or a config object
39312  * @param {String} content (optional) Set the HTML content for this panel
39313  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39314  */
39315 Roo.bootstrap.panel.Content = function( config){
39316     
39317     this.tpl = config.tpl || false;
39318     
39319     var el = config.el;
39320     var content = config.content;
39321
39322     if(config.autoCreate){ // xtype is available if this is called from factory
39323         el = Roo.id();
39324     }
39325     this.el = Roo.get(el);
39326     if(!this.el && config && config.autoCreate){
39327         if(typeof config.autoCreate == "object"){
39328             if(!config.autoCreate.id){
39329                 config.autoCreate.id = config.id||el;
39330             }
39331             this.el = Roo.DomHelper.append(document.body,
39332                         config.autoCreate, true);
39333         }else{
39334             var elcfg =  {
39335                 tag: "div",
39336                 cls: (config.cls || '') +
39337                     (config.background ? ' bg-' + config.background : '') +
39338                     " roo-layout-inactive-content",
39339                 id: config.id||el
39340             };
39341             if (config.html) {
39342                 elcfg.html = config.html;
39343                 
39344             }
39345                         
39346             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39347         }
39348     } 
39349     this.closable = false;
39350     this.loaded = false;
39351     this.active = false;
39352    
39353       
39354     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39355         
39356         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39357         
39358         this.wrapEl = this.el; //this.el.wrap();
39359         var ti = [];
39360         if (config.toolbar.items) {
39361             ti = config.toolbar.items ;
39362             delete config.toolbar.items ;
39363         }
39364         
39365         var nitems = [];
39366         this.toolbar.render(this.wrapEl, 'before');
39367         for(var i =0;i < ti.length;i++) {
39368           //  Roo.log(['add child', items[i]]);
39369             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39370         }
39371         this.toolbar.items = nitems;
39372         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39373         delete config.toolbar;
39374         
39375     }
39376     /*
39377     // xtype created footer. - not sure if will work as we normally have to render first..
39378     if (this.footer && !this.footer.el && this.footer.xtype) {
39379         if (!this.wrapEl) {
39380             this.wrapEl = this.el.wrap();
39381         }
39382     
39383         this.footer.container = this.wrapEl.createChild();
39384          
39385         this.footer = Roo.factory(this.footer, Roo);
39386         
39387     }
39388     */
39389     
39390      if(typeof config == "string"){
39391         this.title = config;
39392     }else{
39393         Roo.apply(this, config);
39394     }
39395     
39396     if(this.resizeEl){
39397         this.resizeEl = Roo.get(this.resizeEl, true);
39398     }else{
39399         this.resizeEl = this.el;
39400     }
39401     // handle view.xtype
39402     
39403  
39404     
39405     
39406     this.addEvents({
39407         /**
39408          * @event activate
39409          * Fires when this panel is activated. 
39410          * @param {Roo.ContentPanel} this
39411          */
39412         "activate" : true,
39413         /**
39414          * @event deactivate
39415          * Fires when this panel is activated. 
39416          * @param {Roo.ContentPanel} this
39417          */
39418         "deactivate" : true,
39419
39420         /**
39421          * @event resize
39422          * Fires when this panel is resized if fitToFrame is true.
39423          * @param {Roo.ContentPanel} this
39424          * @param {Number} width The width after any component adjustments
39425          * @param {Number} height The height after any component adjustments
39426          */
39427         "resize" : true,
39428         
39429          /**
39430          * @event render
39431          * Fires when this tab is created
39432          * @param {Roo.ContentPanel} this
39433          */
39434         "render" : true
39435         
39436         
39437         
39438     });
39439     
39440
39441     
39442     
39443     if(this.autoScroll){
39444         this.resizeEl.setStyle("overflow", "auto");
39445     } else {
39446         // fix randome scrolling
39447         //this.el.on('scroll', function() {
39448         //    Roo.log('fix random scolling');
39449         //    this.scrollTo('top',0); 
39450         //});
39451     }
39452     content = content || this.content;
39453     if(content){
39454         this.setContent(content);
39455     }
39456     if(config && config.url){
39457         this.setUrl(this.url, this.params, this.loadOnce);
39458     }
39459     
39460     
39461     
39462     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39463     
39464     if (this.view && typeof(this.view.xtype) != 'undefined') {
39465         this.view.el = this.el.appendChild(document.createElement("div"));
39466         this.view = Roo.factory(this.view); 
39467         this.view.render  &&  this.view.render(false, '');  
39468     }
39469     
39470     
39471     this.fireEvent('render', this);
39472 };
39473
39474 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39475     
39476     cls : '',
39477     background : '',
39478     
39479     tabTip : '',
39480     
39481     setRegion : function(region){
39482         this.region = region;
39483         this.setActiveClass(region && !this.background);
39484     },
39485     
39486     
39487     setActiveClass: function(state)
39488     {
39489         if(state){
39490            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39491            this.el.setStyle('position','relative');
39492         }else{
39493            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39494            this.el.setStyle('position', 'absolute');
39495         } 
39496     },
39497     
39498     /**
39499      * Returns the toolbar for this Panel if one was configured. 
39500      * @return {Roo.Toolbar} 
39501      */
39502     getToolbar : function(){
39503         return this.toolbar;
39504     },
39505     
39506     setActiveState : function(active)
39507     {
39508         this.active = active;
39509         this.setActiveClass(active);
39510         if(!active){
39511             if(this.fireEvent("deactivate", this) === false){
39512                 return false;
39513             }
39514             return true;
39515         }
39516         this.fireEvent("activate", this);
39517         return true;
39518     },
39519     /**
39520      * Updates this panel's element
39521      * @param {String} content The new content
39522      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39523     */
39524     setContent : function(content, loadScripts){
39525         this.el.update(content, loadScripts);
39526     },
39527
39528     ignoreResize : function(w, h){
39529         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39530             return true;
39531         }else{
39532             this.lastSize = {width: w, height: h};
39533             return false;
39534         }
39535     },
39536     /**
39537      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39538      * @return {Roo.UpdateManager} The UpdateManager
39539      */
39540     getUpdateManager : function(){
39541         return this.el.getUpdateManager();
39542     },
39543      /**
39544      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39545      * @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:
39546 <pre><code>
39547 panel.load({
39548     url: "your-url.php",
39549     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39550     callback: yourFunction,
39551     scope: yourObject, //(optional scope)
39552     discardUrl: false,
39553     nocache: false,
39554     text: "Loading...",
39555     timeout: 30,
39556     scripts: false
39557 });
39558 </code></pre>
39559      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39560      * 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.
39561      * @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}
39562      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39563      * @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.
39564      * @return {Roo.ContentPanel} this
39565      */
39566     load : function(){
39567         var um = this.el.getUpdateManager();
39568         um.update.apply(um, arguments);
39569         return this;
39570     },
39571
39572
39573     /**
39574      * 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.
39575      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39576      * @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)
39577      * @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)
39578      * @return {Roo.UpdateManager} The UpdateManager
39579      */
39580     setUrl : function(url, params, loadOnce){
39581         if(this.refreshDelegate){
39582             this.removeListener("activate", this.refreshDelegate);
39583         }
39584         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39585         this.on("activate", this.refreshDelegate);
39586         return this.el.getUpdateManager();
39587     },
39588     
39589     _handleRefresh : function(url, params, loadOnce){
39590         if(!loadOnce || !this.loaded){
39591             var updater = this.el.getUpdateManager();
39592             updater.update(url, params, this._setLoaded.createDelegate(this));
39593         }
39594     },
39595     
39596     _setLoaded : function(){
39597         this.loaded = true;
39598     }, 
39599     
39600     /**
39601      * Returns this panel's id
39602      * @return {String} 
39603      */
39604     getId : function(){
39605         return this.el.id;
39606     },
39607     
39608     /** 
39609      * Returns this panel's element - used by regiosn to add.
39610      * @return {Roo.Element} 
39611      */
39612     getEl : function(){
39613         return this.wrapEl || this.el;
39614     },
39615     
39616    
39617     
39618     adjustForComponents : function(width, height)
39619     {
39620         //Roo.log('adjustForComponents ');
39621         if(this.resizeEl != this.el){
39622             width -= this.el.getFrameWidth('lr');
39623             height -= this.el.getFrameWidth('tb');
39624         }
39625         if(this.toolbar){
39626             var te = this.toolbar.getEl();
39627             te.setWidth(width);
39628             height -= te.getHeight();
39629         }
39630         if(this.footer){
39631             var te = this.footer.getEl();
39632             te.setWidth(width);
39633             height -= te.getHeight();
39634         }
39635         
39636         
39637         if(this.adjustments){
39638             width += this.adjustments[0];
39639             height += this.adjustments[1];
39640         }
39641         return {"width": width, "height": height};
39642     },
39643     
39644     setSize : function(width, height){
39645         if(this.fitToFrame && !this.ignoreResize(width, height)){
39646             if(this.fitContainer && this.resizeEl != this.el){
39647                 this.el.setSize(width, height);
39648             }
39649             var size = this.adjustForComponents(width, height);
39650             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39651             this.fireEvent('resize', this, size.width, size.height);
39652         }
39653     },
39654     
39655     /**
39656      * Returns this panel's title
39657      * @return {String} 
39658      */
39659     getTitle : function(){
39660         
39661         if (typeof(this.title) != 'object') {
39662             return this.title;
39663         }
39664         
39665         var t = '';
39666         for (var k in this.title) {
39667             if (!this.title.hasOwnProperty(k)) {
39668                 continue;
39669             }
39670             
39671             if (k.indexOf('-') >= 0) {
39672                 var s = k.split('-');
39673                 for (var i = 0; i<s.length; i++) {
39674                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39675                 }
39676             } else {
39677                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39678             }
39679         }
39680         return t;
39681     },
39682     
39683     /**
39684      * Set this panel's title
39685      * @param {String} title
39686      */
39687     setTitle : function(title){
39688         this.title = title;
39689         if(this.region){
39690             this.region.updatePanelTitle(this, title);
39691         }
39692     },
39693     
39694     /**
39695      * Returns true is this panel was configured to be closable
39696      * @return {Boolean} 
39697      */
39698     isClosable : function(){
39699         return this.closable;
39700     },
39701     
39702     beforeSlide : function(){
39703         this.el.clip();
39704         this.resizeEl.clip();
39705     },
39706     
39707     afterSlide : function(){
39708         this.el.unclip();
39709         this.resizeEl.unclip();
39710     },
39711     
39712     /**
39713      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39714      *   Will fail silently if the {@link #setUrl} method has not been called.
39715      *   This does not activate the panel, just updates its content.
39716      */
39717     refresh : function(){
39718         if(this.refreshDelegate){
39719            this.loaded = false;
39720            this.refreshDelegate();
39721         }
39722     },
39723     
39724     /**
39725      * Destroys this panel
39726      */
39727     destroy : function(){
39728         this.el.removeAllListeners();
39729         var tempEl = document.createElement("span");
39730         tempEl.appendChild(this.el.dom);
39731         tempEl.innerHTML = "";
39732         this.el.remove();
39733         this.el = null;
39734     },
39735     
39736     /**
39737      * form - if the content panel contains a form - this is a reference to it.
39738      * @type {Roo.form.Form}
39739      */
39740     form : false,
39741     /**
39742      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39743      *    This contains a reference to it.
39744      * @type {Roo.View}
39745      */
39746     view : false,
39747     
39748       /**
39749      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39750      * <pre><code>
39751
39752 layout.addxtype({
39753        xtype : 'Form',
39754        items: [ .... ]
39755    }
39756 );
39757
39758 </code></pre>
39759      * @param {Object} cfg Xtype definition of item to add.
39760      */
39761     
39762     
39763     getChildContainer: function () {
39764         return this.getEl();
39765     }
39766     
39767     
39768     /*
39769         var  ret = new Roo.factory(cfg);
39770         return ret;
39771         
39772         
39773         // add form..
39774         if (cfg.xtype.match(/^Form$/)) {
39775             
39776             var el;
39777             //if (this.footer) {
39778             //    el = this.footer.container.insertSibling(false, 'before');
39779             //} else {
39780                 el = this.el.createChild();
39781             //}
39782
39783             this.form = new  Roo.form.Form(cfg);
39784             
39785             
39786             if ( this.form.allItems.length) {
39787                 this.form.render(el.dom);
39788             }
39789             return this.form;
39790         }
39791         // should only have one of theses..
39792         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39793             // views.. should not be just added - used named prop 'view''
39794             
39795             cfg.el = this.el.appendChild(document.createElement("div"));
39796             // factory?
39797             
39798             var ret = new Roo.factory(cfg);
39799              
39800              ret.render && ret.render(false, ''); // render blank..
39801             this.view = ret;
39802             return ret;
39803         }
39804         return false;
39805     }
39806     \*/
39807 });
39808  
39809 /**
39810  * @class Roo.bootstrap.panel.Grid
39811  * @extends Roo.bootstrap.panel.Content
39812  * @constructor
39813  * Create a new GridPanel.
39814  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39815  * @param {Object} config A the config object
39816   
39817  */
39818
39819
39820
39821 Roo.bootstrap.panel.Grid = function(config)
39822 {
39823     
39824       
39825     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39826         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39827
39828     config.el = this.wrapper;
39829     //this.el = this.wrapper;
39830     
39831       if (config.container) {
39832         // ctor'ed from a Border/panel.grid
39833         
39834         
39835         this.wrapper.setStyle("overflow", "hidden");
39836         this.wrapper.addClass('roo-grid-container');
39837
39838     }
39839     
39840     
39841     if(config.toolbar){
39842         var tool_el = this.wrapper.createChild();    
39843         this.toolbar = Roo.factory(config.toolbar);
39844         var ti = [];
39845         if (config.toolbar.items) {
39846             ti = config.toolbar.items ;
39847             delete config.toolbar.items ;
39848         }
39849         
39850         var nitems = [];
39851         this.toolbar.render(tool_el);
39852         for(var i =0;i < ti.length;i++) {
39853           //  Roo.log(['add child', items[i]]);
39854             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39855         }
39856         this.toolbar.items = nitems;
39857         
39858         delete config.toolbar;
39859     }
39860     
39861     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39862     config.grid.scrollBody = true;;
39863     config.grid.monitorWindowResize = false; // turn off autosizing
39864     config.grid.autoHeight = false;
39865     config.grid.autoWidth = false;
39866     
39867     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39868     
39869     if (config.background) {
39870         // render grid on panel activation (if panel background)
39871         this.on('activate', function(gp) {
39872             if (!gp.grid.rendered) {
39873                 gp.grid.render(this.wrapper);
39874                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39875             }
39876         });
39877             
39878     } else {
39879         this.grid.render(this.wrapper);
39880         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39881
39882     }
39883     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39884     // ??? needed ??? config.el = this.wrapper;
39885     
39886     
39887     
39888   
39889     // xtype created footer. - not sure if will work as we normally have to render first..
39890     if (this.footer && !this.footer.el && this.footer.xtype) {
39891         
39892         var ctr = this.grid.getView().getFooterPanel(true);
39893         this.footer.dataSource = this.grid.dataSource;
39894         this.footer = Roo.factory(this.footer, Roo);
39895         this.footer.render(ctr);
39896         
39897     }
39898     
39899     
39900     
39901     
39902      
39903 };
39904
39905 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39906     getId : function(){
39907         return this.grid.id;
39908     },
39909     
39910     /**
39911      * Returns the grid for this panel
39912      * @return {Roo.bootstrap.Table} 
39913      */
39914     getGrid : function(){
39915         return this.grid;    
39916     },
39917     
39918     setSize : function(width, height){
39919         if(!this.ignoreResize(width, height)){
39920             var grid = this.grid;
39921             var size = this.adjustForComponents(width, height);
39922             // tfoot is not a footer?
39923           
39924             
39925             var gridel = grid.getGridEl();
39926             gridel.setSize(size.width, size.height);
39927             
39928             var tbd = grid.getGridEl().select('tbody', true).first();
39929             var thd = grid.getGridEl().select('thead',true).first();
39930             var tbf= grid.getGridEl().select('tfoot', true).first();
39931
39932             if (tbf) {
39933                 size.height -= thd.getHeight();
39934             }
39935             if (thd) {
39936                 size.height -= thd.getHeight();
39937             }
39938             
39939             tbd.setSize(size.width, size.height );
39940             // this is for the account management tab -seems to work there.
39941             var thd = grid.getGridEl().select('thead',true).first();
39942             //if (tbd) {
39943             //    tbd.setSize(size.width, size.height - thd.getHeight());
39944             //}
39945              
39946             grid.autoSize();
39947         }
39948     },
39949      
39950     
39951     
39952     beforeSlide : function(){
39953         this.grid.getView().scroller.clip();
39954     },
39955     
39956     afterSlide : function(){
39957         this.grid.getView().scroller.unclip();
39958     },
39959     
39960     destroy : function(){
39961         this.grid.destroy();
39962         delete this.grid;
39963         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39964     }
39965 });
39966
39967 /**
39968  * @class Roo.bootstrap.panel.Nest
39969  * @extends Roo.bootstrap.panel.Content
39970  * @constructor
39971  * Create a new Panel, that can contain a layout.Border.
39972  * 
39973  * 
39974  * @param {Roo.BorderLayout} layout The layout for this panel
39975  * @param {String/Object} config A string to set only the title or a config object
39976  */
39977 Roo.bootstrap.panel.Nest = function(config)
39978 {
39979     // construct with only one argument..
39980     /* FIXME - implement nicer consturctors
39981     if (layout.layout) {
39982         config = layout;
39983         layout = config.layout;
39984         delete config.layout;
39985     }
39986     if (layout.xtype && !layout.getEl) {
39987         // then layout needs constructing..
39988         layout = Roo.factory(layout, Roo);
39989     }
39990     */
39991     
39992     config.el =  config.layout.getEl();
39993     
39994     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39995     
39996     config.layout.monitorWindowResize = false; // turn off autosizing
39997     this.layout = config.layout;
39998     this.layout.getEl().addClass("roo-layout-nested-layout");
39999     this.layout.parent = this;
40000     
40001     
40002     
40003     
40004 };
40005
40006 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40007
40008     setSize : function(width, height){
40009         if(!this.ignoreResize(width, height)){
40010             var size = this.adjustForComponents(width, height);
40011             var el = this.layout.getEl();
40012             if (size.height < 1) {
40013                 el.setWidth(size.width);   
40014             } else {
40015                 el.setSize(size.width, size.height);
40016             }
40017             var touch = el.dom.offsetWidth;
40018             this.layout.layout();
40019             // ie requires a double layout on the first pass
40020             if(Roo.isIE && !this.initialized){
40021                 this.initialized = true;
40022                 this.layout.layout();
40023             }
40024         }
40025     },
40026     
40027     // activate all subpanels if not currently active..
40028     
40029     setActiveState : function(active){
40030         this.active = active;
40031         this.setActiveClass(active);
40032         
40033         if(!active){
40034             this.fireEvent("deactivate", this);
40035             return;
40036         }
40037         
40038         this.fireEvent("activate", this);
40039         // not sure if this should happen before or after..
40040         if (!this.layout) {
40041             return; // should not happen..
40042         }
40043         var reg = false;
40044         for (var r in this.layout.regions) {
40045             reg = this.layout.getRegion(r);
40046             if (reg.getActivePanel()) {
40047                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40048                 reg.setActivePanel(reg.getActivePanel());
40049                 continue;
40050             }
40051             if (!reg.panels.length) {
40052                 continue;
40053             }
40054             reg.showPanel(reg.getPanel(0));
40055         }
40056         
40057         
40058         
40059         
40060     },
40061     
40062     /**
40063      * Returns the nested BorderLayout for this panel
40064      * @return {Roo.BorderLayout} 
40065      */
40066     getLayout : function(){
40067         return this.layout;
40068     },
40069     
40070      /**
40071      * Adds a xtype elements to the layout of the nested panel
40072      * <pre><code>
40073
40074 panel.addxtype({
40075        xtype : 'ContentPanel',
40076        region: 'west',
40077        items: [ .... ]
40078    }
40079 );
40080
40081 panel.addxtype({
40082         xtype : 'NestedLayoutPanel',
40083         region: 'west',
40084         layout: {
40085            center: { },
40086            west: { }   
40087         },
40088         items : [ ... list of content panels or nested layout panels.. ]
40089    }
40090 );
40091 </code></pre>
40092      * @param {Object} cfg Xtype definition of item to add.
40093      */
40094     addxtype : function(cfg) {
40095         return this.layout.addxtype(cfg);
40096     
40097     }
40098 });/*
40099  * Based on:
40100  * Ext JS Library 1.1.1
40101  * Copyright(c) 2006-2007, Ext JS, LLC.
40102  *
40103  * Originally Released Under LGPL - original licence link has changed is not relivant.
40104  *
40105  * Fork - LGPL
40106  * <script type="text/javascript">
40107  */
40108 /**
40109  * @class Roo.TabPanel
40110  * @extends Roo.util.Observable
40111  * A lightweight tab container.
40112  * <br><br>
40113  * Usage:
40114  * <pre><code>
40115 // basic tabs 1, built from existing content
40116 var tabs = new Roo.TabPanel("tabs1");
40117 tabs.addTab("script", "View Script");
40118 tabs.addTab("markup", "View Markup");
40119 tabs.activate("script");
40120
40121 // more advanced tabs, built from javascript
40122 var jtabs = new Roo.TabPanel("jtabs");
40123 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40124
40125 // set up the UpdateManager
40126 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40127 var updater = tab2.getUpdateManager();
40128 updater.setDefaultUrl("ajax1.htm");
40129 tab2.on('activate', updater.refresh, updater, true);
40130
40131 // Use setUrl for Ajax loading
40132 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40133 tab3.setUrl("ajax2.htm", null, true);
40134
40135 // Disabled tab
40136 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40137 tab4.disable();
40138
40139 jtabs.activate("jtabs-1");
40140  * </code></pre>
40141  * @constructor
40142  * Create a new TabPanel.
40143  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40144  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40145  */
40146 Roo.bootstrap.panel.Tabs = function(config){
40147     /**
40148     * The container element for this TabPanel.
40149     * @type Roo.Element
40150     */
40151     this.el = Roo.get(config.el);
40152     delete config.el;
40153     if(config){
40154         if(typeof config == "boolean"){
40155             this.tabPosition = config ? "bottom" : "top";
40156         }else{
40157             Roo.apply(this, config);
40158         }
40159     }
40160     
40161     if(this.tabPosition == "bottom"){
40162         // if tabs are at the bottom = create the body first.
40163         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40164         this.el.addClass("roo-tabs-bottom");
40165     }
40166     // next create the tabs holders
40167     
40168     if (this.tabPosition == "west"){
40169         
40170         var reg = this.region; // fake it..
40171         while (reg) {
40172             if (!reg.mgr.parent) {
40173                 break;
40174             }
40175             reg = reg.mgr.parent.region;
40176         }
40177         Roo.log("got nest?");
40178         Roo.log(reg);
40179         if (reg.mgr.getRegion('west')) {
40180             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40181             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40182             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40183             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40184             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40185         
40186             
40187         }
40188         
40189         
40190     } else {
40191      
40192         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40193         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40194         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40195         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40196     }
40197     
40198     
40199     if(Roo.isIE){
40200         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40201     }
40202     
40203     // finally - if tabs are at the top, then create the body last..
40204     if(this.tabPosition != "bottom"){
40205         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40206          * @type Roo.Element
40207          */
40208         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40209         this.el.addClass("roo-tabs-top");
40210     }
40211     this.items = [];
40212
40213     this.bodyEl.setStyle("position", "relative");
40214
40215     this.active = null;
40216     this.activateDelegate = this.activate.createDelegate(this);
40217
40218     this.addEvents({
40219         /**
40220          * @event tabchange
40221          * Fires when the active tab changes
40222          * @param {Roo.TabPanel} this
40223          * @param {Roo.TabPanelItem} activePanel The new active tab
40224          */
40225         "tabchange": true,
40226         /**
40227          * @event beforetabchange
40228          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40229          * @param {Roo.TabPanel} this
40230          * @param {Object} e Set cancel to true on this object to cancel the tab change
40231          * @param {Roo.TabPanelItem} tab The tab being changed to
40232          */
40233         "beforetabchange" : true
40234     });
40235
40236     Roo.EventManager.onWindowResize(this.onResize, this);
40237     this.cpad = this.el.getPadding("lr");
40238     this.hiddenCount = 0;
40239
40240
40241     // toolbar on the tabbar support...
40242     if (this.toolbar) {
40243         alert("no toolbar support yet");
40244         this.toolbar  = false;
40245         /*
40246         var tcfg = this.toolbar;
40247         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40248         this.toolbar = new Roo.Toolbar(tcfg);
40249         if (Roo.isSafari) {
40250             var tbl = tcfg.container.child('table', true);
40251             tbl.setAttribute('width', '100%');
40252         }
40253         */
40254         
40255     }
40256    
40257
40258
40259     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40260 };
40261
40262 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40263     /*
40264      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40265      */
40266     tabPosition : "top",
40267     /*
40268      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40269      */
40270     currentTabWidth : 0,
40271     /*
40272      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40273      */
40274     minTabWidth : 40,
40275     /*
40276      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40277      */
40278     maxTabWidth : 250,
40279     /*
40280      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40281      */
40282     preferredTabWidth : 175,
40283     /*
40284      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40285      */
40286     resizeTabs : false,
40287     /*
40288      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40289      */
40290     monitorResize : true,
40291     /*
40292      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40293      */
40294     toolbar : false,  // set by caller..
40295     
40296     region : false, /// set by caller
40297     
40298     disableTooltips : true, // not used yet...
40299
40300     /**
40301      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40302      * @param {String} id The id of the div to use <b>or create</b>
40303      * @param {String} text The text for the tab
40304      * @param {String} content (optional) Content to put in the TabPanelItem body
40305      * @param {Boolean} closable (optional) True to create a close icon on the tab
40306      * @return {Roo.TabPanelItem} The created TabPanelItem
40307      */
40308     addTab : function(id, text, content, closable, tpl)
40309     {
40310         var item = new Roo.bootstrap.panel.TabItem({
40311             panel: this,
40312             id : id,
40313             text : text,
40314             closable : closable,
40315             tpl : tpl
40316         });
40317         this.addTabItem(item);
40318         if(content){
40319             item.setContent(content);
40320         }
40321         return item;
40322     },
40323
40324     /**
40325      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40326      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40327      * @return {Roo.TabPanelItem}
40328      */
40329     getTab : function(id){
40330         return this.items[id];
40331     },
40332
40333     /**
40334      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40335      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40336      */
40337     hideTab : function(id){
40338         var t = this.items[id];
40339         if(!t.isHidden()){
40340            t.setHidden(true);
40341            this.hiddenCount++;
40342            this.autoSizeTabs();
40343         }
40344     },
40345
40346     /**
40347      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40348      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40349      */
40350     unhideTab : function(id){
40351         var t = this.items[id];
40352         if(t.isHidden()){
40353            t.setHidden(false);
40354            this.hiddenCount--;
40355            this.autoSizeTabs();
40356         }
40357     },
40358
40359     /**
40360      * Adds an existing {@link Roo.TabPanelItem}.
40361      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40362      */
40363     addTabItem : function(item)
40364     {
40365         this.items[item.id] = item;
40366         this.items.push(item);
40367         this.autoSizeTabs();
40368       //  if(this.resizeTabs){
40369     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40370   //         this.autoSizeTabs();
40371 //        }else{
40372 //            item.autoSize();
40373        // }
40374     },
40375
40376     /**
40377      * Removes a {@link Roo.TabPanelItem}.
40378      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40379      */
40380     removeTab : function(id){
40381         var items = this.items;
40382         var tab = items[id];
40383         if(!tab) { return; }
40384         var index = items.indexOf(tab);
40385         if(this.active == tab && items.length > 1){
40386             var newTab = this.getNextAvailable(index);
40387             if(newTab) {
40388                 newTab.activate();
40389             }
40390         }
40391         this.stripEl.dom.removeChild(tab.pnode.dom);
40392         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40393             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40394         }
40395         items.splice(index, 1);
40396         delete this.items[tab.id];
40397         tab.fireEvent("close", tab);
40398         tab.purgeListeners();
40399         this.autoSizeTabs();
40400     },
40401
40402     getNextAvailable : function(start){
40403         var items = this.items;
40404         var index = start;
40405         // look for a next tab that will slide over to
40406         // replace the one being removed
40407         while(index < items.length){
40408             var item = items[++index];
40409             if(item && !item.isHidden()){
40410                 return item;
40411             }
40412         }
40413         // if one isn't found select the previous tab (on the left)
40414         index = start;
40415         while(index >= 0){
40416             var item = items[--index];
40417             if(item && !item.isHidden()){
40418                 return item;
40419             }
40420         }
40421         return null;
40422     },
40423
40424     /**
40425      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40426      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40427      */
40428     disableTab : function(id){
40429         var tab = this.items[id];
40430         if(tab && this.active != tab){
40431             tab.disable();
40432         }
40433     },
40434
40435     /**
40436      * Enables a {@link Roo.TabPanelItem} that is disabled.
40437      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40438      */
40439     enableTab : function(id){
40440         var tab = this.items[id];
40441         tab.enable();
40442     },
40443
40444     /**
40445      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40446      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40447      * @return {Roo.TabPanelItem} The TabPanelItem.
40448      */
40449     activate : function(id)
40450     {
40451         //Roo.log('activite:'  + id);
40452         
40453         var tab = this.items[id];
40454         if(!tab){
40455             return null;
40456         }
40457         if(tab == this.active || tab.disabled){
40458             return tab;
40459         }
40460         var e = {};
40461         this.fireEvent("beforetabchange", this, e, tab);
40462         if(e.cancel !== true && !tab.disabled){
40463             if(this.active){
40464                 this.active.hide();
40465             }
40466             this.active = this.items[id];
40467             this.active.show();
40468             this.fireEvent("tabchange", this, this.active);
40469         }
40470         return tab;
40471     },
40472
40473     /**
40474      * Gets the active {@link Roo.TabPanelItem}.
40475      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40476      */
40477     getActiveTab : function(){
40478         return this.active;
40479     },
40480
40481     /**
40482      * Updates the tab body element to fit the height of the container element
40483      * for overflow scrolling
40484      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40485      */
40486     syncHeight : function(targetHeight){
40487         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40488         var bm = this.bodyEl.getMargins();
40489         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40490         this.bodyEl.setHeight(newHeight);
40491         return newHeight;
40492     },
40493
40494     onResize : function(){
40495         if(this.monitorResize){
40496             this.autoSizeTabs();
40497         }
40498     },
40499
40500     /**
40501      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40502      */
40503     beginUpdate : function(){
40504         this.updating = true;
40505     },
40506
40507     /**
40508      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40509      */
40510     endUpdate : function(){
40511         this.updating = false;
40512         this.autoSizeTabs();
40513     },
40514
40515     /**
40516      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40517      */
40518     autoSizeTabs : function()
40519     {
40520         var count = this.items.length;
40521         var vcount = count - this.hiddenCount;
40522         
40523         if (vcount < 2) {
40524             this.stripEl.hide();
40525         } else {
40526             this.stripEl.show();
40527         }
40528         
40529         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40530             return;
40531         }
40532         
40533         
40534         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40535         var availWidth = Math.floor(w / vcount);
40536         var b = this.stripBody;
40537         if(b.getWidth() > w){
40538             var tabs = this.items;
40539             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40540             if(availWidth < this.minTabWidth){
40541                 /*if(!this.sleft){    // incomplete scrolling code
40542                     this.createScrollButtons();
40543                 }
40544                 this.showScroll();
40545                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40546             }
40547         }else{
40548             if(this.currentTabWidth < this.preferredTabWidth){
40549                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40550             }
40551         }
40552     },
40553
40554     /**
40555      * Returns the number of tabs in this TabPanel.
40556      * @return {Number}
40557      */
40558      getCount : function(){
40559          return this.items.length;
40560      },
40561
40562     /**
40563      * Resizes all the tabs to the passed width
40564      * @param {Number} The new width
40565      */
40566     setTabWidth : function(width){
40567         this.currentTabWidth = width;
40568         for(var i = 0, len = this.items.length; i < len; i++) {
40569                 if(!this.items[i].isHidden()) {
40570                 this.items[i].setWidth(width);
40571             }
40572         }
40573     },
40574
40575     /**
40576      * Destroys this TabPanel
40577      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40578      */
40579     destroy : function(removeEl){
40580         Roo.EventManager.removeResizeListener(this.onResize, this);
40581         for(var i = 0, len = this.items.length; i < len; i++){
40582             this.items[i].purgeListeners();
40583         }
40584         if(removeEl === true){
40585             this.el.update("");
40586             this.el.remove();
40587         }
40588     },
40589     
40590     createStrip : function(container)
40591     {
40592         var strip = document.createElement("nav");
40593         strip.className = Roo.bootstrap.version == 4 ?
40594             "navbar-light bg-light" : 
40595             "navbar navbar-default"; //"x-tabs-wrap";
40596         container.appendChild(strip);
40597         return strip;
40598     },
40599     
40600     createStripList : function(strip)
40601     {
40602         // div wrapper for retard IE
40603         // returns the "tr" element.
40604         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40605         //'<div class="x-tabs-strip-wrap">'+
40606           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40607           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40608         return strip.firstChild; //.firstChild.firstChild.firstChild;
40609     },
40610     createBody : function(container)
40611     {
40612         var body = document.createElement("div");
40613         Roo.id(body, "tab-body");
40614         //Roo.fly(body).addClass("x-tabs-body");
40615         Roo.fly(body).addClass("tab-content");
40616         container.appendChild(body);
40617         return body;
40618     },
40619     createItemBody :function(bodyEl, id){
40620         var body = Roo.getDom(id);
40621         if(!body){
40622             body = document.createElement("div");
40623             body.id = id;
40624         }
40625         //Roo.fly(body).addClass("x-tabs-item-body");
40626         Roo.fly(body).addClass("tab-pane");
40627          bodyEl.insertBefore(body, bodyEl.firstChild);
40628         return body;
40629     },
40630     /** @private */
40631     createStripElements :  function(stripEl, text, closable, tpl)
40632     {
40633         var td = document.createElement("li"); // was td..
40634         td.className = 'nav-item';
40635         
40636         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40637         
40638         
40639         stripEl.appendChild(td);
40640         /*if(closable){
40641             td.className = "x-tabs-closable";
40642             if(!this.closeTpl){
40643                 this.closeTpl = new Roo.Template(
40644                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40645                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40646                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40647                 );
40648             }
40649             var el = this.closeTpl.overwrite(td, {"text": text});
40650             var close = el.getElementsByTagName("div")[0];
40651             var inner = el.getElementsByTagName("em")[0];
40652             return {"el": el, "close": close, "inner": inner};
40653         } else {
40654         */
40655         // not sure what this is..
40656 //            if(!this.tabTpl){
40657                 //this.tabTpl = new Roo.Template(
40658                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40659                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40660                 //);
40661 //                this.tabTpl = new Roo.Template(
40662 //                   '<a href="#">' +
40663 //                   '<span unselectable="on"' +
40664 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40665 //                            ' >{text}</span></a>'
40666 //                );
40667 //                
40668 //            }
40669
40670
40671             var template = tpl || this.tabTpl || false;
40672             
40673             if(!template){
40674                 template =  new Roo.Template(
40675                         Roo.bootstrap.version == 4 ? 
40676                             (
40677                                 '<a class="nav-link" href="#" unselectable="on"' +
40678                                      (this.disableTooltips ? '' : ' title="{text}"') +
40679                                      ' >{text}</a>'
40680                             ) : (
40681                                 '<a class="nav-link" href="#">' +
40682                                 '<span unselectable="on"' +
40683                                          (this.disableTooltips ? '' : ' title="{text}"') +
40684                                     ' >{text}</span></a>'
40685                             )
40686                 );
40687             }
40688             
40689             switch (typeof(template)) {
40690                 case 'object' :
40691                     break;
40692                 case 'string' :
40693                     template = new Roo.Template(template);
40694                     break;
40695                 default :
40696                     break;
40697             }
40698             
40699             var el = template.overwrite(td, {"text": text});
40700             
40701             var inner = el.getElementsByTagName("span")[0];
40702             
40703             return {"el": el, "inner": inner};
40704             
40705     }
40706         
40707     
40708 });
40709
40710 /**
40711  * @class Roo.TabPanelItem
40712  * @extends Roo.util.Observable
40713  * Represents an individual item (tab plus body) in a TabPanel.
40714  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40715  * @param {String} id The id of this TabPanelItem
40716  * @param {String} text The text for the tab of this TabPanelItem
40717  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40718  */
40719 Roo.bootstrap.panel.TabItem = function(config){
40720     /**
40721      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40722      * @type Roo.TabPanel
40723      */
40724     this.tabPanel = config.panel;
40725     /**
40726      * The id for this TabPanelItem
40727      * @type String
40728      */
40729     this.id = config.id;
40730     /** @private */
40731     this.disabled = false;
40732     /** @private */
40733     this.text = config.text;
40734     /** @private */
40735     this.loaded = false;
40736     this.closable = config.closable;
40737
40738     /**
40739      * The body element for this TabPanelItem.
40740      * @type Roo.Element
40741      */
40742     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40743     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40744     this.bodyEl.setStyle("display", "block");
40745     this.bodyEl.setStyle("zoom", "1");
40746     //this.hideAction();
40747
40748     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40749     /** @private */
40750     this.el = Roo.get(els.el);
40751     this.inner = Roo.get(els.inner, true);
40752      this.textEl = Roo.bootstrap.version == 4 ?
40753         this.el : Roo.get(this.el.dom.firstChild, true);
40754
40755     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40756     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40757
40758     
40759 //    this.el.on("mousedown", this.onTabMouseDown, this);
40760     this.el.on("click", this.onTabClick, this);
40761     /** @private */
40762     if(config.closable){
40763         var c = Roo.get(els.close, true);
40764         c.dom.title = this.closeText;
40765         c.addClassOnOver("close-over");
40766         c.on("click", this.closeClick, this);
40767      }
40768
40769     this.addEvents({
40770          /**
40771          * @event activate
40772          * Fires when this tab becomes the active tab.
40773          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40774          * @param {Roo.TabPanelItem} this
40775          */
40776         "activate": true,
40777         /**
40778          * @event beforeclose
40779          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40780          * @param {Roo.TabPanelItem} this
40781          * @param {Object} e Set cancel to true on this object to cancel the close.
40782          */
40783         "beforeclose": true,
40784         /**
40785          * @event close
40786          * Fires when this tab is closed.
40787          * @param {Roo.TabPanelItem} this
40788          */
40789          "close": true,
40790         /**
40791          * @event deactivate
40792          * Fires when this tab is no longer the active tab.
40793          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40794          * @param {Roo.TabPanelItem} this
40795          */
40796          "deactivate" : true
40797     });
40798     this.hidden = false;
40799
40800     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40801 };
40802
40803 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40804            {
40805     purgeListeners : function(){
40806        Roo.util.Observable.prototype.purgeListeners.call(this);
40807        this.el.removeAllListeners();
40808     },
40809     /**
40810      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40811      */
40812     show : function(){
40813         this.status_node.addClass("active");
40814         this.showAction();
40815         if(Roo.isOpera){
40816             this.tabPanel.stripWrap.repaint();
40817         }
40818         this.fireEvent("activate", this.tabPanel, this);
40819     },
40820
40821     /**
40822      * Returns true if this tab is the active tab.
40823      * @return {Boolean}
40824      */
40825     isActive : function(){
40826         return this.tabPanel.getActiveTab() == this;
40827     },
40828
40829     /**
40830      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40831      */
40832     hide : function(){
40833         this.status_node.removeClass("active");
40834         this.hideAction();
40835         this.fireEvent("deactivate", this.tabPanel, this);
40836     },
40837
40838     hideAction : function(){
40839         this.bodyEl.hide();
40840         this.bodyEl.setStyle("position", "absolute");
40841         this.bodyEl.setLeft("-20000px");
40842         this.bodyEl.setTop("-20000px");
40843     },
40844
40845     showAction : function(){
40846         this.bodyEl.setStyle("position", "relative");
40847         this.bodyEl.setTop("");
40848         this.bodyEl.setLeft("");
40849         this.bodyEl.show();
40850     },
40851
40852     /**
40853      * Set the tooltip for the tab.
40854      * @param {String} tooltip The tab's tooltip
40855      */
40856     setTooltip : function(text){
40857         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40858             this.textEl.dom.qtip = text;
40859             this.textEl.dom.removeAttribute('title');
40860         }else{
40861             this.textEl.dom.title = text;
40862         }
40863     },
40864
40865     onTabClick : function(e){
40866         e.preventDefault();
40867         this.tabPanel.activate(this.id);
40868     },
40869
40870     onTabMouseDown : function(e){
40871         e.preventDefault();
40872         this.tabPanel.activate(this.id);
40873     },
40874 /*
40875     getWidth : function(){
40876         return this.inner.getWidth();
40877     },
40878
40879     setWidth : function(width){
40880         var iwidth = width - this.linode.getPadding("lr");
40881         this.inner.setWidth(iwidth);
40882         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40883         this.linode.setWidth(width);
40884     },
40885 */
40886     /**
40887      * Show or hide the tab
40888      * @param {Boolean} hidden True to hide or false to show.
40889      */
40890     setHidden : function(hidden){
40891         this.hidden = hidden;
40892         this.linode.setStyle("display", hidden ? "none" : "");
40893     },
40894
40895     /**
40896      * Returns true if this tab is "hidden"
40897      * @return {Boolean}
40898      */
40899     isHidden : function(){
40900         return this.hidden;
40901     },
40902
40903     /**
40904      * Returns the text for this tab
40905      * @return {String}
40906      */
40907     getText : function(){
40908         return this.text;
40909     },
40910     /*
40911     autoSize : function(){
40912         //this.el.beginMeasure();
40913         this.textEl.setWidth(1);
40914         /*
40915          *  #2804 [new] Tabs in Roojs
40916          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40917          */
40918         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40919         //this.el.endMeasure();
40920     //},
40921
40922     /**
40923      * Sets the text for the tab (Note: this also sets the tooltip text)
40924      * @param {String} text The tab's text and tooltip
40925      */
40926     setText : function(text){
40927         this.text = text;
40928         this.textEl.update(text);
40929         this.setTooltip(text);
40930         //if(!this.tabPanel.resizeTabs){
40931         //    this.autoSize();
40932         //}
40933     },
40934     /**
40935      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40936      */
40937     activate : function(){
40938         this.tabPanel.activate(this.id);
40939     },
40940
40941     /**
40942      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40943      */
40944     disable : function(){
40945         if(this.tabPanel.active != this){
40946             this.disabled = true;
40947             this.status_node.addClass("disabled");
40948         }
40949     },
40950
40951     /**
40952      * Enables this TabPanelItem if it was previously disabled.
40953      */
40954     enable : function(){
40955         this.disabled = false;
40956         this.status_node.removeClass("disabled");
40957     },
40958
40959     /**
40960      * Sets the content for this TabPanelItem.
40961      * @param {String} content The content
40962      * @param {Boolean} loadScripts true to look for and load scripts
40963      */
40964     setContent : function(content, loadScripts){
40965         this.bodyEl.update(content, loadScripts);
40966     },
40967
40968     /**
40969      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40970      * @return {Roo.UpdateManager} The UpdateManager
40971      */
40972     getUpdateManager : function(){
40973         return this.bodyEl.getUpdateManager();
40974     },
40975
40976     /**
40977      * Set a URL to be used to load the content for this TabPanelItem.
40978      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40979      * @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)
40980      * @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)
40981      * @return {Roo.UpdateManager} The UpdateManager
40982      */
40983     setUrl : function(url, params, loadOnce){
40984         if(this.refreshDelegate){
40985             this.un('activate', this.refreshDelegate);
40986         }
40987         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40988         this.on("activate", this.refreshDelegate);
40989         return this.bodyEl.getUpdateManager();
40990     },
40991
40992     /** @private */
40993     _handleRefresh : function(url, params, loadOnce){
40994         if(!loadOnce || !this.loaded){
40995             var updater = this.bodyEl.getUpdateManager();
40996             updater.update(url, params, this._setLoaded.createDelegate(this));
40997         }
40998     },
40999
41000     /**
41001      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41002      *   Will fail silently if the setUrl method has not been called.
41003      *   This does not activate the panel, just updates its content.
41004      */
41005     refresh : function(){
41006         if(this.refreshDelegate){
41007            this.loaded = false;
41008            this.refreshDelegate();
41009         }
41010     },
41011
41012     /** @private */
41013     _setLoaded : function(){
41014         this.loaded = true;
41015     },
41016
41017     /** @private */
41018     closeClick : function(e){
41019         var o = {};
41020         e.stopEvent();
41021         this.fireEvent("beforeclose", this, o);
41022         if(o.cancel !== true){
41023             this.tabPanel.removeTab(this.id);
41024         }
41025     },
41026     /**
41027      * The text displayed in the tooltip for the close icon.
41028      * @type String
41029      */
41030     closeText : "Close this tab"
41031 });
41032 /**
41033 *    This script refer to:
41034 *    Title: International Telephone Input
41035 *    Author: Jack O'Connor
41036 *    Code version:  v12.1.12
41037 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41038 **/
41039
41040 Roo.bootstrap.PhoneInputData = function() {
41041     var d = [
41042       [
41043         "Afghanistan (‫افغانستان‬‎)",
41044         "af",
41045         "93"
41046       ],
41047       [
41048         "Albania (Shqipëri)",
41049         "al",
41050         "355"
41051       ],
41052       [
41053         "Algeria (‫الجزائر‬‎)",
41054         "dz",
41055         "213"
41056       ],
41057       [
41058         "American Samoa",
41059         "as",
41060         "1684"
41061       ],
41062       [
41063         "Andorra",
41064         "ad",
41065         "376"
41066       ],
41067       [
41068         "Angola",
41069         "ao",
41070         "244"
41071       ],
41072       [
41073         "Anguilla",
41074         "ai",
41075         "1264"
41076       ],
41077       [
41078         "Antigua and Barbuda",
41079         "ag",
41080         "1268"
41081       ],
41082       [
41083         "Argentina",
41084         "ar",
41085         "54"
41086       ],
41087       [
41088         "Armenia (Հայաստան)",
41089         "am",
41090         "374"
41091       ],
41092       [
41093         "Aruba",
41094         "aw",
41095         "297"
41096       ],
41097       [
41098         "Australia",
41099         "au",
41100         "61",
41101         0
41102       ],
41103       [
41104         "Austria (Österreich)",
41105         "at",
41106         "43"
41107       ],
41108       [
41109         "Azerbaijan (Azərbaycan)",
41110         "az",
41111         "994"
41112       ],
41113       [
41114         "Bahamas",
41115         "bs",
41116         "1242"
41117       ],
41118       [
41119         "Bahrain (‫البحرين‬‎)",
41120         "bh",
41121         "973"
41122       ],
41123       [
41124         "Bangladesh (বাংলাদেশ)",
41125         "bd",
41126         "880"
41127       ],
41128       [
41129         "Barbados",
41130         "bb",
41131         "1246"
41132       ],
41133       [
41134         "Belarus (Беларусь)",
41135         "by",
41136         "375"
41137       ],
41138       [
41139         "Belgium (België)",
41140         "be",
41141         "32"
41142       ],
41143       [
41144         "Belize",
41145         "bz",
41146         "501"
41147       ],
41148       [
41149         "Benin (Bénin)",
41150         "bj",
41151         "229"
41152       ],
41153       [
41154         "Bermuda",
41155         "bm",
41156         "1441"
41157       ],
41158       [
41159         "Bhutan (འབྲུག)",
41160         "bt",
41161         "975"
41162       ],
41163       [
41164         "Bolivia",
41165         "bo",
41166         "591"
41167       ],
41168       [
41169         "Bosnia and Herzegovina (Босна и Херцеговина)",
41170         "ba",
41171         "387"
41172       ],
41173       [
41174         "Botswana",
41175         "bw",
41176         "267"
41177       ],
41178       [
41179         "Brazil (Brasil)",
41180         "br",
41181         "55"
41182       ],
41183       [
41184         "British Indian Ocean Territory",
41185         "io",
41186         "246"
41187       ],
41188       [
41189         "British Virgin Islands",
41190         "vg",
41191         "1284"
41192       ],
41193       [
41194         "Brunei",
41195         "bn",
41196         "673"
41197       ],
41198       [
41199         "Bulgaria (България)",
41200         "bg",
41201         "359"
41202       ],
41203       [
41204         "Burkina Faso",
41205         "bf",
41206         "226"
41207       ],
41208       [
41209         "Burundi (Uburundi)",
41210         "bi",
41211         "257"
41212       ],
41213       [
41214         "Cambodia (កម្ពុជា)",
41215         "kh",
41216         "855"
41217       ],
41218       [
41219         "Cameroon (Cameroun)",
41220         "cm",
41221         "237"
41222       ],
41223       [
41224         "Canada",
41225         "ca",
41226         "1",
41227         1,
41228         ["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"]
41229       ],
41230       [
41231         "Cape Verde (Kabu Verdi)",
41232         "cv",
41233         "238"
41234       ],
41235       [
41236         "Caribbean Netherlands",
41237         "bq",
41238         "599",
41239         1
41240       ],
41241       [
41242         "Cayman Islands",
41243         "ky",
41244         "1345"
41245       ],
41246       [
41247         "Central African Republic (République centrafricaine)",
41248         "cf",
41249         "236"
41250       ],
41251       [
41252         "Chad (Tchad)",
41253         "td",
41254         "235"
41255       ],
41256       [
41257         "Chile",
41258         "cl",
41259         "56"
41260       ],
41261       [
41262         "China (中国)",
41263         "cn",
41264         "86"
41265       ],
41266       [
41267         "Christmas Island",
41268         "cx",
41269         "61",
41270         2
41271       ],
41272       [
41273         "Cocos (Keeling) Islands",
41274         "cc",
41275         "61",
41276         1
41277       ],
41278       [
41279         "Colombia",
41280         "co",
41281         "57"
41282       ],
41283       [
41284         "Comoros (‫جزر القمر‬‎)",
41285         "km",
41286         "269"
41287       ],
41288       [
41289         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41290         "cd",
41291         "243"
41292       ],
41293       [
41294         "Congo (Republic) (Congo-Brazzaville)",
41295         "cg",
41296         "242"
41297       ],
41298       [
41299         "Cook Islands",
41300         "ck",
41301         "682"
41302       ],
41303       [
41304         "Costa Rica",
41305         "cr",
41306         "506"
41307       ],
41308       [
41309         "Côte d’Ivoire",
41310         "ci",
41311         "225"
41312       ],
41313       [
41314         "Croatia (Hrvatska)",
41315         "hr",
41316         "385"
41317       ],
41318       [
41319         "Cuba",
41320         "cu",
41321         "53"
41322       ],
41323       [
41324         "Curaçao",
41325         "cw",
41326         "599",
41327         0
41328       ],
41329       [
41330         "Cyprus (Κύπρος)",
41331         "cy",
41332         "357"
41333       ],
41334       [
41335         "Czech Republic (Česká republika)",
41336         "cz",
41337         "420"
41338       ],
41339       [
41340         "Denmark (Danmark)",
41341         "dk",
41342         "45"
41343       ],
41344       [
41345         "Djibouti",
41346         "dj",
41347         "253"
41348       ],
41349       [
41350         "Dominica",
41351         "dm",
41352         "1767"
41353       ],
41354       [
41355         "Dominican Republic (República Dominicana)",
41356         "do",
41357         "1",
41358         2,
41359         ["809", "829", "849"]
41360       ],
41361       [
41362         "Ecuador",
41363         "ec",
41364         "593"
41365       ],
41366       [
41367         "Egypt (‫مصر‬‎)",
41368         "eg",
41369         "20"
41370       ],
41371       [
41372         "El Salvador",
41373         "sv",
41374         "503"
41375       ],
41376       [
41377         "Equatorial Guinea (Guinea Ecuatorial)",
41378         "gq",
41379         "240"
41380       ],
41381       [
41382         "Eritrea",
41383         "er",
41384         "291"
41385       ],
41386       [
41387         "Estonia (Eesti)",
41388         "ee",
41389         "372"
41390       ],
41391       [
41392         "Ethiopia",
41393         "et",
41394         "251"
41395       ],
41396       [
41397         "Falkland Islands (Islas Malvinas)",
41398         "fk",
41399         "500"
41400       ],
41401       [
41402         "Faroe Islands (Føroyar)",
41403         "fo",
41404         "298"
41405       ],
41406       [
41407         "Fiji",
41408         "fj",
41409         "679"
41410       ],
41411       [
41412         "Finland (Suomi)",
41413         "fi",
41414         "358",
41415         0
41416       ],
41417       [
41418         "France",
41419         "fr",
41420         "33"
41421       ],
41422       [
41423         "French Guiana (Guyane française)",
41424         "gf",
41425         "594"
41426       ],
41427       [
41428         "French Polynesia (Polynésie française)",
41429         "pf",
41430         "689"
41431       ],
41432       [
41433         "Gabon",
41434         "ga",
41435         "241"
41436       ],
41437       [
41438         "Gambia",
41439         "gm",
41440         "220"
41441       ],
41442       [
41443         "Georgia (საქართველო)",
41444         "ge",
41445         "995"
41446       ],
41447       [
41448         "Germany (Deutschland)",
41449         "de",
41450         "49"
41451       ],
41452       [
41453         "Ghana (Gaana)",
41454         "gh",
41455         "233"
41456       ],
41457       [
41458         "Gibraltar",
41459         "gi",
41460         "350"
41461       ],
41462       [
41463         "Greece (Ελλάδα)",
41464         "gr",
41465         "30"
41466       ],
41467       [
41468         "Greenland (Kalaallit Nunaat)",
41469         "gl",
41470         "299"
41471       ],
41472       [
41473         "Grenada",
41474         "gd",
41475         "1473"
41476       ],
41477       [
41478         "Guadeloupe",
41479         "gp",
41480         "590",
41481         0
41482       ],
41483       [
41484         "Guam",
41485         "gu",
41486         "1671"
41487       ],
41488       [
41489         "Guatemala",
41490         "gt",
41491         "502"
41492       ],
41493       [
41494         "Guernsey",
41495         "gg",
41496         "44",
41497         1
41498       ],
41499       [
41500         "Guinea (Guinée)",
41501         "gn",
41502         "224"
41503       ],
41504       [
41505         "Guinea-Bissau (Guiné Bissau)",
41506         "gw",
41507         "245"
41508       ],
41509       [
41510         "Guyana",
41511         "gy",
41512         "592"
41513       ],
41514       [
41515         "Haiti",
41516         "ht",
41517         "509"
41518       ],
41519       [
41520         "Honduras",
41521         "hn",
41522         "504"
41523       ],
41524       [
41525         "Hong Kong (香港)",
41526         "hk",
41527         "852"
41528       ],
41529       [
41530         "Hungary (Magyarország)",
41531         "hu",
41532         "36"
41533       ],
41534       [
41535         "Iceland (Ísland)",
41536         "is",
41537         "354"
41538       ],
41539       [
41540         "India (भारत)",
41541         "in",
41542         "91"
41543       ],
41544       [
41545         "Indonesia",
41546         "id",
41547         "62"
41548       ],
41549       [
41550         "Iran (‫ایران‬‎)",
41551         "ir",
41552         "98"
41553       ],
41554       [
41555         "Iraq (‫العراق‬‎)",
41556         "iq",
41557         "964"
41558       ],
41559       [
41560         "Ireland",
41561         "ie",
41562         "353"
41563       ],
41564       [
41565         "Isle of Man",
41566         "im",
41567         "44",
41568         2
41569       ],
41570       [
41571         "Israel (‫ישראל‬‎)",
41572         "il",
41573         "972"
41574       ],
41575       [
41576         "Italy (Italia)",
41577         "it",
41578         "39",
41579         0
41580       ],
41581       [
41582         "Jamaica",
41583         "jm",
41584         "1876"
41585       ],
41586       [
41587         "Japan (日本)",
41588         "jp",
41589         "81"
41590       ],
41591       [
41592         "Jersey",
41593         "je",
41594         "44",
41595         3
41596       ],
41597       [
41598         "Jordan (‫الأردن‬‎)",
41599         "jo",
41600         "962"
41601       ],
41602       [
41603         "Kazakhstan (Казахстан)",
41604         "kz",
41605         "7",
41606         1
41607       ],
41608       [
41609         "Kenya",
41610         "ke",
41611         "254"
41612       ],
41613       [
41614         "Kiribati",
41615         "ki",
41616         "686"
41617       ],
41618       [
41619         "Kosovo",
41620         "xk",
41621         "383"
41622       ],
41623       [
41624         "Kuwait (‫الكويت‬‎)",
41625         "kw",
41626         "965"
41627       ],
41628       [
41629         "Kyrgyzstan (Кыргызстан)",
41630         "kg",
41631         "996"
41632       ],
41633       [
41634         "Laos (ລາວ)",
41635         "la",
41636         "856"
41637       ],
41638       [
41639         "Latvia (Latvija)",
41640         "lv",
41641         "371"
41642       ],
41643       [
41644         "Lebanon (‫لبنان‬‎)",
41645         "lb",
41646         "961"
41647       ],
41648       [
41649         "Lesotho",
41650         "ls",
41651         "266"
41652       ],
41653       [
41654         "Liberia",
41655         "lr",
41656         "231"
41657       ],
41658       [
41659         "Libya (‫ليبيا‬‎)",
41660         "ly",
41661         "218"
41662       ],
41663       [
41664         "Liechtenstein",
41665         "li",
41666         "423"
41667       ],
41668       [
41669         "Lithuania (Lietuva)",
41670         "lt",
41671         "370"
41672       ],
41673       [
41674         "Luxembourg",
41675         "lu",
41676         "352"
41677       ],
41678       [
41679         "Macau (澳門)",
41680         "mo",
41681         "853"
41682       ],
41683       [
41684         "Macedonia (FYROM) (Македонија)",
41685         "mk",
41686         "389"
41687       ],
41688       [
41689         "Madagascar (Madagasikara)",
41690         "mg",
41691         "261"
41692       ],
41693       [
41694         "Malawi",
41695         "mw",
41696         "265"
41697       ],
41698       [
41699         "Malaysia",
41700         "my",
41701         "60"
41702       ],
41703       [
41704         "Maldives",
41705         "mv",
41706         "960"
41707       ],
41708       [
41709         "Mali",
41710         "ml",
41711         "223"
41712       ],
41713       [
41714         "Malta",
41715         "mt",
41716         "356"
41717       ],
41718       [
41719         "Marshall Islands",
41720         "mh",
41721         "692"
41722       ],
41723       [
41724         "Martinique",
41725         "mq",
41726         "596"
41727       ],
41728       [
41729         "Mauritania (‫موريتانيا‬‎)",
41730         "mr",
41731         "222"
41732       ],
41733       [
41734         "Mauritius (Moris)",
41735         "mu",
41736         "230"
41737       ],
41738       [
41739         "Mayotte",
41740         "yt",
41741         "262",
41742         1
41743       ],
41744       [
41745         "Mexico (México)",
41746         "mx",
41747         "52"
41748       ],
41749       [
41750         "Micronesia",
41751         "fm",
41752         "691"
41753       ],
41754       [
41755         "Moldova (Republica Moldova)",
41756         "md",
41757         "373"
41758       ],
41759       [
41760         "Monaco",
41761         "mc",
41762         "377"
41763       ],
41764       [
41765         "Mongolia (Монгол)",
41766         "mn",
41767         "976"
41768       ],
41769       [
41770         "Montenegro (Crna Gora)",
41771         "me",
41772         "382"
41773       ],
41774       [
41775         "Montserrat",
41776         "ms",
41777         "1664"
41778       ],
41779       [
41780         "Morocco (‫المغرب‬‎)",
41781         "ma",
41782         "212",
41783         0
41784       ],
41785       [
41786         "Mozambique (Moçambique)",
41787         "mz",
41788         "258"
41789       ],
41790       [
41791         "Myanmar (Burma) (မြန်မာ)",
41792         "mm",
41793         "95"
41794       ],
41795       [
41796         "Namibia (Namibië)",
41797         "na",
41798         "264"
41799       ],
41800       [
41801         "Nauru",
41802         "nr",
41803         "674"
41804       ],
41805       [
41806         "Nepal (नेपाल)",
41807         "np",
41808         "977"
41809       ],
41810       [
41811         "Netherlands (Nederland)",
41812         "nl",
41813         "31"
41814       ],
41815       [
41816         "New Caledonia (Nouvelle-Calédonie)",
41817         "nc",
41818         "687"
41819       ],
41820       [
41821         "New Zealand",
41822         "nz",
41823         "64"
41824       ],
41825       [
41826         "Nicaragua",
41827         "ni",
41828         "505"
41829       ],
41830       [
41831         "Niger (Nijar)",
41832         "ne",
41833         "227"
41834       ],
41835       [
41836         "Nigeria",
41837         "ng",
41838         "234"
41839       ],
41840       [
41841         "Niue",
41842         "nu",
41843         "683"
41844       ],
41845       [
41846         "Norfolk Island",
41847         "nf",
41848         "672"
41849       ],
41850       [
41851         "North Korea (조선 민주주의 인민 공화국)",
41852         "kp",
41853         "850"
41854       ],
41855       [
41856         "Northern Mariana Islands",
41857         "mp",
41858         "1670"
41859       ],
41860       [
41861         "Norway (Norge)",
41862         "no",
41863         "47",
41864         0
41865       ],
41866       [
41867         "Oman (‫عُمان‬‎)",
41868         "om",
41869         "968"
41870       ],
41871       [
41872         "Pakistan (‫پاکستان‬‎)",
41873         "pk",
41874         "92"
41875       ],
41876       [
41877         "Palau",
41878         "pw",
41879         "680"
41880       ],
41881       [
41882         "Palestine (‫فلسطين‬‎)",
41883         "ps",
41884         "970"
41885       ],
41886       [
41887         "Panama (Panamá)",
41888         "pa",
41889         "507"
41890       ],
41891       [
41892         "Papua New Guinea",
41893         "pg",
41894         "675"
41895       ],
41896       [
41897         "Paraguay",
41898         "py",
41899         "595"
41900       ],
41901       [
41902         "Peru (Perú)",
41903         "pe",
41904         "51"
41905       ],
41906       [
41907         "Philippines",
41908         "ph",
41909         "63"
41910       ],
41911       [
41912         "Poland (Polska)",
41913         "pl",
41914         "48"
41915       ],
41916       [
41917         "Portugal",
41918         "pt",
41919         "351"
41920       ],
41921       [
41922         "Puerto Rico",
41923         "pr",
41924         "1",
41925         3,
41926         ["787", "939"]
41927       ],
41928       [
41929         "Qatar (‫قطر‬‎)",
41930         "qa",
41931         "974"
41932       ],
41933       [
41934         "Réunion (La Réunion)",
41935         "re",
41936         "262",
41937         0
41938       ],
41939       [
41940         "Romania (România)",
41941         "ro",
41942         "40"
41943       ],
41944       [
41945         "Russia (Россия)",
41946         "ru",
41947         "7",
41948         0
41949       ],
41950       [
41951         "Rwanda",
41952         "rw",
41953         "250"
41954       ],
41955       [
41956         "Saint Barthélemy",
41957         "bl",
41958         "590",
41959         1
41960       ],
41961       [
41962         "Saint Helena",
41963         "sh",
41964         "290"
41965       ],
41966       [
41967         "Saint Kitts and Nevis",
41968         "kn",
41969         "1869"
41970       ],
41971       [
41972         "Saint Lucia",
41973         "lc",
41974         "1758"
41975       ],
41976       [
41977         "Saint Martin (Saint-Martin (partie française))",
41978         "mf",
41979         "590",
41980         2
41981       ],
41982       [
41983         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41984         "pm",
41985         "508"
41986       ],
41987       [
41988         "Saint Vincent and the Grenadines",
41989         "vc",
41990         "1784"
41991       ],
41992       [
41993         "Samoa",
41994         "ws",
41995         "685"
41996       ],
41997       [
41998         "San Marino",
41999         "sm",
42000         "378"
42001       ],
42002       [
42003         "São Tomé and Príncipe (São Tomé e Príncipe)",
42004         "st",
42005         "239"
42006       ],
42007       [
42008         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42009         "sa",
42010         "966"
42011       ],
42012       [
42013         "Senegal (Sénégal)",
42014         "sn",
42015         "221"
42016       ],
42017       [
42018         "Serbia (Србија)",
42019         "rs",
42020         "381"
42021       ],
42022       [
42023         "Seychelles",
42024         "sc",
42025         "248"
42026       ],
42027       [
42028         "Sierra Leone",
42029         "sl",
42030         "232"
42031       ],
42032       [
42033         "Singapore",
42034         "sg",
42035         "65"
42036       ],
42037       [
42038         "Sint Maarten",
42039         "sx",
42040         "1721"
42041       ],
42042       [
42043         "Slovakia (Slovensko)",
42044         "sk",
42045         "421"
42046       ],
42047       [
42048         "Slovenia (Slovenija)",
42049         "si",
42050         "386"
42051       ],
42052       [
42053         "Solomon Islands",
42054         "sb",
42055         "677"
42056       ],
42057       [
42058         "Somalia (Soomaaliya)",
42059         "so",
42060         "252"
42061       ],
42062       [
42063         "South Africa",
42064         "za",
42065         "27"
42066       ],
42067       [
42068         "South Korea (대한민국)",
42069         "kr",
42070         "82"
42071       ],
42072       [
42073         "South Sudan (‫جنوب السودان‬‎)",
42074         "ss",
42075         "211"
42076       ],
42077       [
42078         "Spain (España)",
42079         "es",
42080         "34"
42081       ],
42082       [
42083         "Sri Lanka (ශ්‍රී ලංකාව)",
42084         "lk",
42085         "94"
42086       ],
42087       [
42088         "Sudan (‫السودان‬‎)",
42089         "sd",
42090         "249"
42091       ],
42092       [
42093         "Suriname",
42094         "sr",
42095         "597"
42096       ],
42097       [
42098         "Svalbard and Jan Mayen",
42099         "sj",
42100         "47",
42101         1
42102       ],
42103       [
42104         "Swaziland",
42105         "sz",
42106         "268"
42107       ],
42108       [
42109         "Sweden (Sverige)",
42110         "se",
42111         "46"
42112       ],
42113       [
42114         "Switzerland (Schweiz)",
42115         "ch",
42116         "41"
42117       ],
42118       [
42119         "Syria (‫سوريا‬‎)",
42120         "sy",
42121         "963"
42122       ],
42123       [
42124         "Taiwan (台灣)",
42125         "tw",
42126         "886"
42127       ],
42128       [
42129         "Tajikistan",
42130         "tj",
42131         "992"
42132       ],
42133       [
42134         "Tanzania",
42135         "tz",
42136         "255"
42137       ],
42138       [
42139         "Thailand (ไทย)",
42140         "th",
42141         "66"
42142       ],
42143       [
42144         "Timor-Leste",
42145         "tl",
42146         "670"
42147       ],
42148       [
42149         "Togo",
42150         "tg",
42151         "228"
42152       ],
42153       [
42154         "Tokelau",
42155         "tk",
42156         "690"
42157       ],
42158       [
42159         "Tonga",
42160         "to",
42161         "676"
42162       ],
42163       [
42164         "Trinidad and Tobago",
42165         "tt",
42166         "1868"
42167       ],
42168       [
42169         "Tunisia (‫تونس‬‎)",
42170         "tn",
42171         "216"
42172       ],
42173       [
42174         "Turkey (Türkiye)",
42175         "tr",
42176         "90"
42177       ],
42178       [
42179         "Turkmenistan",
42180         "tm",
42181         "993"
42182       ],
42183       [
42184         "Turks and Caicos Islands",
42185         "tc",
42186         "1649"
42187       ],
42188       [
42189         "Tuvalu",
42190         "tv",
42191         "688"
42192       ],
42193       [
42194         "U.S. Virgin Islands",
42195         "vi",
42196         "1340"
42197       ],
42198       [
42199         "Uganda",
42200         "ug",
42201         "256"
42202       ],
42203       [
42204         "Ukraine (Україна)",
42205         "ua",
42206         "380"
42207       ],
42208       [
42209         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42210         "ae",
42211         "971"
42212       ],
42213       [
42214         "United Kingdom",
42215         "gb",
42216         "44",
42217         0
42218       ],
42219       [
42220         "United States",
42221         "us",
42222         "1",
42223         0
42224       ],
42225       [
42226         "Uruguay",
42227         "uy",
42228         "598"
42229       ],
42230       [
42231         "Uzbekistan (Oʻzbekiston)",
42232         "uz",
42233         "998"
42234       ],
42235       [
42236         "Vanuatu",
42237         "vu",
42238         "678"
42239       ],
42240       [
42241         "Vatican City (Città del Vaticano)",
42242         "va",
42243         "39",
42244         1
42245       ],
42246       [
42247         "Venezuela",
42248         "ve",
42249         "58"
42250       ],
42251       [
42252         "Vietnam (Việt Nam)",
42253         "vn",
42254         "84"
42255       ],
42256       [
42257         "Wallis and Futuna (Wallis-et-Futuna)",
42258         "wf",
42259         "681"
42260       ],
42261       [
42262         "Western Sahara (‫الصحراء الغربية‬‎)",
42263         "eh",
42264         "212",
42265         1
42266       ],
42267       [
42268         "Yemen (‫اليمن‬‎)",
42269         "ye",
42270         "967"
42271       ],
42272       [
42273         "Zambia",
42274         "zm",
42275         "260"
42276       ],
42277       [
42278         "Zimbabwe",
42279         "zw",
42280         "263"
42281       ],
42282       [
42283         "Åland Islands",
42284         "ax",
42285         "358",
42286         1
42287       ]
42288   ];
42289   
42290   return d;
42291 }/**
42292 *    This script refer to:
42293 *    Title: International Telephone Input
42294 *    Author: Jack O'Connor
42295 *    Code version:  v12.1.12
42296 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42297 **/
42298
42299 /**
42300  * @class Roo.bootstrap.PhoneInput
42301  * @extends Roo.bootstrap.TriggerField
42302  * An input with International dial-code selection
42303  
42304  * @cfg {String} defaultDialCode default '+852'
42305  * @cfg {Array} preferedCountries default []
42306   
42307  * @constructor
42308  * Create a new PhoneInput.
42309  * @param {Object} config Configuration options
42310  */
42311
42312 Roo.bootstrap.PhoneInput = function(config) {
42313     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42314 };
42315
42316 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42317         
42318         listWidth: undefined,
42319         
42320         selectedClass: 'active',
42321         
42322         invalidClass : "has-warning",
42323         
42324         validClass: 'has-success',
42325         
42326         allowed: '0123456789',
42327         
42328         max_length: 15,
42329         
42330         /**
42331          * @cfg {String} defaultDialCode The default dial code when initializing the input
42332          */
42333         defaultDialCode: '+852',
42334         
42335         /**
42336          * @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
42337          */
42338         preferedCountries: false,
42339         
42340         getAutoCreate : function()
42341         {
42342             var data = Roo.bootstrap.PhoneInputData();
42343             var align = this.labelAlign || this.parentLabelAlign();
42344             var id = Roo.id();
42345             
42346             this.allCountries = [];
42347             this.dialCodeMapping = [];
42348             
42349             for (var i = 0; i < data.length; i++) {
42350               var c = data[i];
42351               this.allCountries[i] = {
42352                 name: c[0],
42353                 iso2: c[1],
42354                 dialCode: c[2],
42355                 priority: c[3] || 0,
42356                 areaCodes: c[4] || null
42357               };
42358               this.dialCodeMapping[c[2]] = {
42359                   name: c[0],
42360                   iso2: c[1],
42361                   priority: c[3] || 0,
42362                   areaCodes: c[4] || null
42363               };
42364             }
42365             
42366             var cfg = {
42367                 cls: 'form-group',
42368                 cn: []
42369             };
42370             
42371             var input =  {
42372                 tag: 'input',
42373                 id : id,
42374                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42375                 maxlength: this.max_length,
42376                 cls : 'form-control tel-input',
42377                 autocomplete: 'new-password'
42378             };
42379             
42380             var hiddenInput = {
42381                 tag: 'input',
42382                 type: 'hidden',
42383                 cls: 'hidden-tel-input'
42384             };
42385             
42386             if (this.name) {
42387                 hiddenInput.name = this.name;
42388             }
42389             
42390             if (this.disabled) {
42391                 input.disabled = true;
42392             }
42393             
42394             var flag_container = {
42395                 tag: 'div',
42396                 cls: 'flag-box',
42397                 cn: [
42398                     {
42399                         tag: 'div',
42400                         cls: 'flag'
42401                     },
42402                     {
42403                         tag: 'div',
42404                         cls: 'caret'
42405                     }
42406                 ]
42407             };
42408             
42409             var box = {
42410                 tag: 'div',
42411                 cls: this.hasFeedback ? 'has-feedback' : '',
42412                 cn: [
42413                     hiddenInput,
42414                     input,
42415                     {
42416                         tag: 'input',
42417                         cls: 'dial-code-holder',
42418                         disabled: true
42419                     }
42420                 ]
42421             };
42422             
42423             var container = {
42424                 cls: 'roo-select2-container input-group',
42425                 cn: [
42426                     flag_container,
42427                     box
42428                 ]
42429             };
42430             
42431             if (this.fieldLabel.length) {
42432                 var indicator = {
42433                     tag: 'i',
42434                     tooltip: 'This field is required'
42435                 };
42436                 
42437                 var label = {
42438                     tag: 'label',
42439                     'for':  id,
42440                     cls: 'control-label',
42441                     cn: []
42442                 };
42443                 
42444                 var label_text = {
42445                     tag: 'span',
42446                     html: this.fieldLabel
42447                 };
42448                 
42449                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42450                 label.cn = [
42451                     indicator,
42452                     label_text
42453                 ];
42454                 
42455                 if(this.indicatorpos == 'right') {
42456                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42457                     label.cn = [
42458                         label_text,
42459                         indicator
42460                     ];
42461                 }
42462                 
42463                 if(align == 'left') {
42464                     container = {
42465                         tag: 'div',
42466                         cn: [
42467                             container
42468                         ]
42469                     };
42470                     
42471                     if(this.labelWidth > 12){
42472                         label.style = "width: " + this.labelWidth + 'px';
42473                     }
42474                     if(this.labelWidth < 13 && this.labelmd == 0){
42475                         this.labelmd = this.labelWidth;
42476                     }
42477                     if(this.labellg > 0){
42478                         label.cls += ' col-lg-' + this.labellg;
42479                         input.cls += ' col-lg-' + (12 - this.labellg);
42480                     }
42481                     if(this.labelmd > 0){
42482                         label.cls += ' col-md-' + this.labelmd;
42483                         container.cls += ' col-md-' + (12 - this.labelmd);
42484                     }
42485                     if(this.labelsm > 0){
42486                         label.cls += ' col-sm-' + this.labelsm;
42487                         container.cls += ' col-sm-' + (12 - this.labelsm);
42488                     }
42489                     if(this.labelxs > 0){
42490                         label.cls += ' col-xs-' + this.labelxs;
42491                         container.cls += ' col-xs-' + (12 - this.labelxs);
42492                     }
42493                 }
42494             }
42495             
42496             cfg.cn = [
42497                 label,
42498                 container
42499             ];
42500             
42501             var settings = this;
42502             
42503             ['xs','sm','md','lg'].map(function(size){
42504                 if (settings[size]) {
42505                     cfg.cls += ' col-' + size + '-' + settings[size];
42506                 }
42507             });
42508             
42509             this.store = new Roo.data.Store({
42510                 proxy : new Roo.data.MemoryProxy({}),
42511                 reader : new Roo.data.JsonReader({
42512                     fields : [
42513                         {
42514                             'name' : 'name',
42515                             'type' : 'string'
42516                         },
42517                         {
42518                             'name' : 'iso2',
42519                             'type' : 'string'
42520                         },
42521                         {
42522                             'name' : 'dialCode',
42523                             'type' : 'string'
42524                         },
42525                         {
42526                             'name' : 'priority',
42527                             'type' : 'string'
42528                         },
42529                         {
42530                             'name' : 'areaCodes',
42531                             'type' : 'string'
42532                         }
42533                     ]
42534                 })
42535             });
42536             
42537             if(!this.preferedCountries) {
42538                 this.preferedCountries = [
42539                     'hk',
42540                     'gb',
42541                     'us'
42542                 ];
42543             }
42544             
42545             var p = this.preferedCountries.reverse();
42546             
42547             if(p) {
42548                 for (var i = 0; i < p.length; i++) {
42549                     for (var j = 0; j < this.allCountries.length; j++) {
42550                         if(this.allCountries[j].iso2 == p[i]) {
42551                             var t = this.allCountries[j];
42552                             this.allCountries.splice(j,1);
42553                             this.allCountries.unshift(t);
42554                         }
42555                     } 
42556                 }
42557             }
42558             
42559             this.store.proxy.data = {
42560                 success: true,
42561                 data: this.allCountries
42562             };
42563             
42564             return cfg;
42565         },
42566         
42567         initEvents : function()
42568         {
42569             this.createList();
42570             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42571             
42572             this.indicator = this.indicatorEl();
42573             this.flag = this.flagEl();
42574             this.dialCodeHolder = this.dialCodeHolderEl();
42575             
42576             this.trigger = this.el.select('div.flag-box',true).first();
42577             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42578             
42579             var _this = this;
42580             
42581             (function(){
42582                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42583                 _this.list.setWidth(lw);
42584             }).defer(100);
42585             
42586             this.list.on('mouseover', this.onViewOver, this);
42587             this.list.on('mousemove', this.onViewMove, this);
42588             this.inputEl().on("keyup", this.onKeyUp, this);
42589             this.inputEl().on("keypress", this.onKeyPress, this);
42590             
42591             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42592
42593             this.view = new Roo.View(this.list, this.tpl, {
42594                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42595             });
42596             
42597             this.view.on('click', this.onViewClick, this);
42598             this.setValue(this.defaultDialCode);
42599         },
42600         
42601         onTriggerClick : function(e)
42602         {
42603             Roo.log('trigger click');
42604             if(this.disabled){
42605                 return;
42606             }
42607             
42608             if(this.isExpanded()){
42609                 this.collapse();
42610                 this.hasFocus = false;
42611             }else {
42612                 this.store.load({});
42613                 this.hasFocus = true;
42614                 this.expand();
42615             }
42616         },
42617         
42618         isExpanded : function()
42619         {
42620             return this.list.isVisible();
42621         },
42622         
42623         collapse : function()
42624         {
42625             if(!this.isExpanded()){
42626                 return;
42627             }
42628             this.list.hide();
42629             Roo.get(document).un('mousedown', this.collapseIf, this);
42630             Roo.get(document).un('mousewheel', this.collapseIf, this);
42631             this.fireEvent('collapse', this);
42632             this.validate();
42633         },
42634         
42635         expand : function()
42636         {
42637             Roo.log('expand');
42638
42639             if(this.isExpanded() || !this.hasFocus){
42640                 return;
42641             }
42642             
42643             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42644             this.list.setWidth(lw);
42645             
42646             this.list.show();
42647             this.restrictHeight();
42648             
42649             Roo.get(document).on('mousedown', this.collapseIf, this);
42650             Roo.get(document).on('mousewheel', this.collapseIf, this);
42651             
42652             this.fireEvent('expand', this);
42653         },
42654         
42655         restrictHeight : function()
42656         {
42657             this.list.alignTo(this.inputEl(), this.listAlign);
42658             this.list.alignTo(this.inputEl(), this.listAlign);
42659         },
42660         
42661         onViewOver : function(e, t)
42662         {
42663             if(this.inKeyMode){
42664                 return;
42665             }
42666             var item = this.view.findItemFromChild(t);
42667             
42668             if(item){
42669                 var index = this.view.indexOf(item);
42670                 this.select(index, false);
42671             }
42672         },
42673
42674         // private
42675         onViewClick : function(view, doFocus, el, e)
42676         {
42677             var index = this.view.getSelectedIndexes()[0];
42678             
42679             var r = this.store.getAt(index);
42680             
42681             if(r){
42682                 this.onSelect(r, index);
42683             }
42684             if(doFocus !== false && !this.blockFocus){
42685                 this.inputEl().focus();
42686             }
42687         },
42688         
42689         onViewMove : function(e, t)
42690         {
42691             this.inKeyMode = false;
42692         },
42693         
42694         select : function(index, scrollIntoView)
42695         {
42696             this.selectedIndex = index;
42697             this.view.select(index);
42698             if(scrollIntoView !== false){
42699                 var el = this.view.getNode(index);
42700                 if(el){
42701                     this.list.scrollChildIntoView(el, false);
42702                 }
42703             }
42704         },
42705         
42706         createList : function()
42707         {
42708             this.list = Roo.get(document.body).createChild({
42709                 tag: 'ul',
42710                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42711                 style: 'display:none'
42712             });
42713             
42714             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42715         },
42716         
42717         collapseIf : function(e)
42718         {
42719             var in_combo  = e.within(this.el);
42720             var in_list =  e.within(this.list);
42721             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42722             
42723             if (in_combo || in_list || is_list) {
42724                 return;
42725             }
42726             this.collapse();
42727         },
42728         
42729         onSelect : function(record, index)
42730         {
42731             if(this.fireEvent('beforeselect', this, record, index) !== false){
42732                 
42733                 this.setFlagClass(record.data.iso2);
42734                 this.setDialCode(record.data.dialCode);
42735                 this.hasFocus = false;
42736                 this.collapse();
42737                 this.fireEvent('select', this, record, index);
42738             }
42739         },
42740         
42741         flagEl : function()
42742         {
42743             var flag = this.el.select('div.flag',true).first();
42744             if(!flag){
42745                 return false;
42746             }
42747             return flag;
42748         },
42749         
42750         dialCodeHolderEl : function()
42751         {
42752             var d = this.el.select('input.dial-code-holder',true).first();
42753             if(!d){
42754                 return false;
42755             }
42756             return d;
42757         },
42758         
42759         setDialCode : function(v)
42760         {
42761             this.dialCodeHolder.dom.value = '+'+v;
42762         },
42763         
42764         setFlagClass : function(n)
42765         {
42766             this.flag.dom.className = 'flag '+n;
42767         },
42768         
42769         getValue : function()
42770         {
42771             var v = this.inputEl().getValue();
42772             if(this.dialCodeHolder) {
42773                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42774             }
42775             return v;
42776         },
42777         
42778         setValue : function(v)
42779         {
42780             var d = this.getDialCode(v);
42781             
42782             //invalid dial code
42783             if(v.length == 0 || !d || d.length == 0) {
42784                 if(this.rendered){
42785                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42786                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42787                 }
42788                 return;
42789             }
42790             
42791             //valid dial code
42792             this.setFlagClass(this.dialCodeMapping[d].iso2);
42793             this.setDialCode(d);
42794             this.inputEl().dom.value = v.replace('+'+d,'');
42795             this.hiddenEl().dom.value = this.getValue();
42796             
42797             this.validate();
42798         },
42799         
42800         getDialCode : function(v)
42801         {
42802             v = v ||  '';
42803             
42804             if (v.length == 0) {
42805                 return this.dialCodeHolder.dom.value;
42806             }
42807             
42808             var dialCode = "";
42809             if (v.charAt(0) != "+") {
42810                 return false;
42811             }
42812             var numericChars = "";
42813             for (var i = 1; i < v.length; i++) {
42814               var c = v.charAt(i);
42815               if (!isNaN(c)) {
42816                 numericChars += c;
42817                 if (this.dialCodeMapping[numericChars]) {
42818                   dialCode = v.substr(1, i);
42819                 }
42820                 if (numericChars.length == 4) {
42821                   break;
42822                 }
42823               }
42824             }
42825             return dialCode;
42826         },
42827         
42828         reset : function()
42829         {
42830             this.setValue(this.defaultDialCode);
42831             this.validate();
42832         },
42833         
42834         hiddenEl : function()
42835         {
42836             return this.el.select('input.hidden-tel-input',true).first();
42837         },
42838         
42839         // after setting val
42840         onKeyUp : function(e){
42841             this.setValue(this.getValue());
42842         },
42843         
42844         onKeyPress : function(e){
42845             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42846                 e.stopEvent();
42847             }
42848         }
42849         
42850 });
42851 /**
42852  * @class Roo.bootstrap.MoneyField
42853  * @extends Roo.bootstrap.ComboBox
42854  * Bootstrap MoneyField class
42855  * 
42856  * @constructor
42857  * Create a new MoneyField.
42858  * @param {Object} config Configuration options
42859  */
42860
42861 Roo.bootstrap.MoneyField = function(config) {
42862     
42863     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42864     
42865 };
42866
42867 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42868     
42869     /**
42870      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42871      */
42872     allowDecimals : true,
42873     /**
42874      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42875      */
42876     decimalSeparator : ".",
42877     /**
42878      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42879      */
42880     decimalPrecision : 0,
42881     /**
42882      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42883      */
42884     allowNegative : true,
42885     /**
42886      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42887      */
42888     allowZero: true,
42889     /**
42890      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42891      */
42892     minValue : Number.NEGATIVE_INFINITY,
42893     /**
42894      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42895      */
42896     maxValue : Number.MAX_VALUE,
42897     /**
42898      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42899      */
42900     minText : "The minimum value for this field is {0}",
42901     /**
42902      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42903      */
42904     maxText : "The maximum value for this field is {0}",
42905     /**
42906      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42907      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42908      */
42909     nanText : "{0} is not a valid number",
42910     /**
42911      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42912      */
42913     castInt : true,
42914     /**
42915      * @cfg {String} defaults currency of the MoneyField
42916      * value should be in lkey
42917      */
42918     defaultCurrency : false,
42919     /**
42920      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42921      */
42922     thousandsDelimiter : false,
42923     /**
42924      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42925      */
42926     max_length: false,
42927     
42928     inputlg : 9,
42929     inputmd : 9,
42930     inputsm : 9,
42931     inputxs : 6,
42932     
42933     store : false,
42934     
42935     getAutoCreate : function()
42936     {
42937         var align = this.labelAlign || this.parentLabelAlign();
42938         
42939         var id = Roo.id();
42940
42941         var cfg = {
42942             cls: 'form-group',
42943             cn: []
42944         };
42945
42946         var input =  {
42947             tag: 'input',
42948             id : id,
42949             cls : 'form-control roo-money-amount-input',
42950             autocomplete: 'new-password'
42951         };
42952         
42953         var hiddenInput = {
42954             tag: 'input',
42955             type: 'hidden',
42956             id: Roo.id(),
42957             cls: 'hidden-number-input'
42958         };
42959         
42960         if(this.max_length) {
42961             input.maxlength = this.max_length; 
42962         }
42963         
42964         if (this.name) {
42965             hiddenInput.name = this.name;
42966         }
42967
42968         if (this.disabled) {
42969             input.disabled = true;
42970         }
42971
42972         var clg = 12 - this.inputlg;
42973         var cmd = 12 - this.inputmd;
42974         var csm = 12 - this.inputsm;
42975         var cxs = 12 - this.inputxs;
42976         
42977         var container = {
42978             tag : 'div',
42979             cls : 'row roo-money-field',
42980             cn : [
42981                 {
42982                     tag : 'div',
42983                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42984                     cn : [
42985                         {
42986                             tag : 'div',
42987                             cls: 'roo-select2-container input-group',
42988                             cn: [
42989                                 {
42990                                     tag : 'input',
42991                                     cls : 'form-control roo-money-currency-input',
42992                                     autocomplete: 'new-password',
42993                                     readOnly : 1,
42994                                     name : this.currencyName
42995                                 },
42996                                 {
42997                                     tag :'span',
42998                                     cls : 'input-group-addon',
42999                                     cn : [
43000                                         {
43001                                             tag: 'span',
43002                                             cls: 'caret'
43003                                         }
43004                                     ]
43005                                 }
43006                             ]
43007                         }
43008                     ]
43009                 },
43010                 {
43011                     tag : 'div',
43012                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43013                     cn : [
43014                         {
43015                             tag: 'div',
43016                             cls: this.hasFeedback ? 'has-feedback' : '',
43017                             cn: [
43018                                 input
43019                             ]
43020                         }
43021                     ]
43022                 }
43023             ]
43024             
43025         };
43026         
43027         if (this.fieldLabel.length) {
43028             var indicator = {
43029                 tag: 'i',
43030                 tooltip: 'This field is required'
43031             };
43032
43033             var label = {
43034                 tag: 'label',
43035                 'for':  id,
43036                 cls: 'control-label',
43037                 cn: []
43038             };
43039
43040             var label_text = {
43041                 tag: 'span',
43042                 html: this.fieldLabel
43043             };
43044
43045             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43046             label.cn = [
43047                 indicator,
43048                 label_text
43049             ];
43050
43051             if(this.indicatorpos == 'right') {
43052                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43053                 label.cn = [
43054                     label_text,
43055                     indicator
43056                 ];
43057             }
43058
43059             if(align == 'left') {
43060                 container = {
43061                     tag: 'div',
43062                     cn: [
43063                         container
43064                     ]
43065                 };
43066
43067                 if(this.labelWidth > 12){
43068                     label.style = "width: " + this.labelWidth + 'px';
43069                 }
43070                 if(this.labelWidth < 13 && this.labelmd == 0){
43071                     this.labelmd = this.labelWidth;
43072                 }
43073                 if(this.labellg > 0){
43074                     label.cls += ' col-lg-' + this.labellg;
43075                     input.cls += ' col-lg-' + (12 - this.labellg);
43076                 }
43077                 if(this.labelmd > 0){
43078                     label.cls += ' col-md-' + this.labelmd;
43079                     container.cls += ' col-md-' + (12 - this.labelmd);
43080                 }
43081                 if(this.labelsm > 0){
43082                     label.cls += ' col-sm-' + this.labelsm;
43083                     container.cls += ' col-sm-' + (12 - this.labelsm);
43084                 }
43085                 if(this.labelxs > 0){
43086                     label.cls += ' col-xs-' + this.labelxs;
43087                     container.cls += ' col-xs-' + (12 - this.labelxs);
43088                 }
43089             }
43090         }
43091
43092         cfg.cn = [
43093             label,
43094             container,
43095             hiddenInput
43096         ];
43097         
43098         var settings = this;
43099
43100         ['xs','sm','md','lg'].map(function(size){
43101             if (settings[size]) {
43102                 cfg.cls += ' col-' + size + '-' + settings[size];
43103             }
43104         });
43105         
43106         return cfg;
43107     },
43108     
43109     initEvents : function()
43110     {
43111         this.indicator = this.indicatorEl();
43112         
43113         this.initCurrencyEvent();
43114         
43115         this.initNumberEvent();
43116     },
43117     
43118     initCurrencyEvent : function()
43119     {
43120         if (!this.store) {
43121             throw "can not find store for combo";
43122         }
43123         
43124         this.store = Roo.factory(this.store, Roo.data);
43125         this.store.parent = this;
43126         
43127         this.createList();
43128         
43129         this.triggerEl = this.el.select('.input-group-addon', true).first();
43130         
43131         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43132         
43133         var _this = this;
43134         
43135         (function(){
43136             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43137             _this.list.setWidth(lw);
43138         }).defer(100);
43139         
43140         this.list.on('mouseover', this.onViewOver, this);
43141         this.list.on('mousemove', this.onViewMove, this);
43142         this.list.on('scroll', this.onViewScroll, this);
43143         
43144         if(!this.tpl){
43145             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43146         }
43147         
43148         this.view = new Roo.View(this.list, this.tpl, {
43149             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43150         });
43151         
43152         this.view.on('click', this.onViewClick, this);
43153         
43154         this.store.on('beforeload', this.onBeforeLoad, this);
43155         this.store.on('load', this.onLoad, this);
43156         this.store.on('loadexception', this.onLoadException, this);
43157         
43158         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43159             "up" : function(e){
43160                 this.inKeyMode = true;
43161                 this.selectPrev();
43162             },
43163
43164             "down" : function(e){
43165                 if(!this.isExpanded()){
43166                     this.onTriggerClick();
43167                 }else{
43168                     this.inKeyMode = true;
43169                     this.selectNext();
43170                 }
43171             },
43172
43173             "enter" : function(e){
43174                 this.collapse();
43175                 
43176                 if(this.fireEvent("specialkey", this, e)){
43177                     this.onViewClick(false);
43178                 }
43179                 
43180                 return true;
43181             },
43182
43183             "esc" : function(e){
43184                 this.collapse();
43185             },
43186
43187             "tab" : function(e){
43188                 this.collapse();
43189                 
43190                 if(this.fireEvent("specialkey", this, e)){
43191                     this.onViewClick(false);
43192                 }
43193                 
43194                 return true;
43195             },
43196
43197             scope : this,
43198
43199             doRelay : function(foo, bar, hname){
43200                 if(hname == 'down' || this.scope.isExpanded()){
43201                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43202                 }
43203                 return true;
43204             },
43205
43206             forceKeyDown: true
43207         });
43208         
43209         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43210         
43211     },
43212     
43213     initNumberEvent : function(e)
43214     {
43215         this.inputEl().on("keydown" , this.fireKey,  this);
43216         this.inputEl().on("focus", this.onFocus,  this);
43217         this.inputEl().on("blur", this.onBlur,  this);
43218         
43219         this.inputEl().relayEvent('keyup', this);
43220         
43221         if(this.indicator){
43222             this.indicator.addClass('invisible');
43223         }
43224  
43225         this.originalValue = this.getValue();
43226         
43227         if(this.validationEvent == 'keyup'){
43228             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43229             this.inputEl().on('keyup', this.filterValidation, this);
43230         }
43231         else if(this.validationEvent !== false){
43232             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43233         }
43234         
43235         if(this.selectOnFocus){
43236             this.on("focus", this.preFocus, this);
43237             
43238         }
43239         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43240             this.inputEl().on("keypress", this.filterKeys, this);
43241         } else {
43242             this.inputEl().relayEvent('keypress', this);
43243         }
43244         
43245         var allowed = "0123456789";
43246         
43247         if(this.allowDecimals){
43248             allowed += this.decimalSeparator;
43249         }
43250         
43251         if(this.allowNegative){
43252             allowed += "-";
43253         }
43254         
43255         if(this.thousandsDelimiter) {
43256             allowed += ",";
43257         }
43258         
43259         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43260         
43261         var keyPress = function(e){
43262             
43263             var k = e.getKey();
43264             
43265             var c = e.getCharCode();
43266             
43267             if(
43268                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43269                     allowed.indexOf(String.fromCharCode(c)) === -1
43270             ){
43271                 e.stopEvent();
43272                 return;
43273             }
43274             
43275             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43276                 return;
43277             }
43278             
43279             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43280                 e.stopEvent();
43281             }
43282         };
43283         
43284         this.inputEl().on("keypress", keyPress, this);
43285         
43286     },
43287     
43288     onTriggerClick : function(e)
43289     {   
43290         if(this.disabled){
43291             return;
43292         }
43293         
43294         this.page = 0;
43295         this.loadNext = false;
43296         
43297         if(this.isExpanded()){
43298             this.collapse();
43299             return;
43300         }
43301         
43302         this.hasFocus = true;
43303         
43304         if(this.triggerAction == 'all') {
43305             this.doQuery(this.allQuery, true);
43306             return;
43307         }
43308         
43309         this.doQuery(this.getRawValue());
43310     },
43311     
43312     getCurrency : function()
43313     {   
43314         var v = this.currencyEl().getValue();
43315         
43316         return v;
43317     },
43318     
43319     restrictHeight : function()
43320     {
43321         this.list.alignTo(this.currencyEl(), this.listAlign);
43322         this.list.alignTo(this.currencyEl(), this.listAlign);
43323     },
43324     
43325     onViewClick : function(view, doFocus, el, e)
43326     {
43327         var index = this.view.getSelectedIndexes()[0];
43328         
43329         var r = this.store.getAt(index);
43330         
43331         if(r){
43332             this.onSelect(r, index);
43333         }
43334     },
43335     
43336     onSelect : function(record, index){
43337         
43338         if(this.fireEvent('beforeselect', this, record, index) !== false){
43339         
43340             this.setFromCurrencyData(index > -1 ? record.data : false);
43341             
43342             this.collapse();
43343             
43344             this.fireEvent('select', this, record, index);
43345         }
43346     },
43347     
43348     setFromCurrencyData : function(o)
43349     {
43350         var currency = '';
43351         
43352         this.lastCurrency = o;
43353         
43354         if (this.currencyField) {
43355             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43356         } else {
43357             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43358         }
43359         
43360         this.lastSelectionText = currency;
43361         
43362         //setting default currency
43363         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43364             this.setCurrency(this.defaultCurrency);
43365             return;
43366         }
43367         
43368         this.setCurrency(currency);
43369     },
43370     
43371     setFromData : function(o)
43372     {
43373         var c = {};
43374         
43375         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43376         
43377         this.setFromCurrencyData(c);
43378         
43379         var value = '';
43380         
43381         if (this.name) {
43382             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43383         } else {
43384             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43385         }
43386         
43387         this.setValue(value);
43388         
43389     },
43390     
43391     setCurrency : function(v)
43392     {   
43393         this.currencyValue = v;
43394         
43395         if(this.rendered){
43396             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43397             this.validate();
43398         }
43399     },
43400     
43401     setValue : function(v)
43402     {
43403         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43404         
43405         this.value = v;
43406         
43407         if(this.rendered){
43408             
43409             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43410             
43411             this.inputEl().dom.value = (v == '') ? '' :
43412                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43413             
43414             if(!this.allowZero && v === '0') {
43415                 this.hiddenEl().dom.value = '';
43416                 this.inputEl().dom.value = '';
43417             }
43418             
43419             this.validate();
43420         }
43421     },
43422     
43423     getRawValue : function()
43424     {
43425         var v = this.inputEl().getValue();
43426         
43427         return v;
43428     },
43429     
43430     getValue : function()
43431     {
43432         return this.fixPrecision(this.parseValue(this.getRawValue()));
43433     },
43434     
43435     parseValue : function(value)
43436     {
43437         if(this.thousandsDelimiter) {
43438             value += "";
43439             r = new RegExp(",", "g");
43440             value = value.replace(r, "");
43441         }
43442         
43443         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43444         return isNaN(value) ? '' : value;
43445         
43446     },
43447     
43448     fixPrecision : function(value)
43449     {
43450         if(this.thousandsDelimiter) {
43451             value += "";
43452             r = new RegExp(",", "g");
43453             value = value.replace(r, "");
43454         }
43455         
43456         var nan = isNaN(value);
43457         
43458         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43459             return nan ? '' : value;
43460         }
43461         return parseFloat(value).toFixed(this.decimalPrecision);
43462     },
43463     
43464     decimalPrecisionFcn : function(v)
43465     {
43466         return Math.floor(v);
43467     },
43468     
43469     validateValue : function(value)
43470     {
43471         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43472             return false;
43473         }
43474         
43475         var num = this.parseValue(value);
43476         
43477         if(isNaN(num)){
43478             this.markInvalid(String.format(this.nanText, value));
43479             return false;
43480         }
43481         
43482         if(num < this.minValue){
43483             this.markInvalid(String.format(this.minText, this.minValue));
43484             return false;
43485         }
43486         
43487         if(num > this.maxValue){
43488             this.markInvalid(String.format(this.maxText, this.maxValue));
43489             return false;
43490         }
43491         
43492         return true;
43493     },
43494     
43495     validate : function()
43496     {
43497         if(this.disabled || this.allowBlank){
43498             this.markValid();
43499             return true;
43500         }
43501         
43502         var currency = this.getCurrency();
43503         
43504         if(this.validateValue(this.getRawValue()) && currency.length){
43505             this.markValid();
43506             return true;
43507         }
43508         
43509         this.markInvalid();
43510         return false;
43511     },
43512     
43513     getName: function()
43514     {
43515         return this.name;
43516     },
43517     
43518     beforeBlur : function()
43519     {
43520         if(!this.castInt){
43521             return;
43522         }
43523         
43524         var v = this.parseValue(this.getRawValue());
43525         
43526         if(v || v == 0){
43527             this.setValue(v);
43528         }
43529     },
43530     
43531     onBlur : function()
43532     {
43533         this.beforeBlur();
43534         
43535         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43536             //this.el.removeClass(this.focusClass);
43537         }
43538         
43539         this.hasFocus = false;
43540         
43541         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43542             this.validate();
43543         }
43544         
43545         var v = this.getValue();
43546         
43547         if(String(v) !== String(this.startValue)){
43548             this.fireEvent('change', this, v, this.startValue);
43549         }
43550         
43551         this.fireEvent("blur", this);
43552     },
43553     
43554     inputEl : function()
43555     {
43556         return this.el.select('.roo-money-amount-input', true).first();
43557     },
43558     
43559     currencyEl : function()
43560     {
43561         return this.el.select('.roo-money-currency-input', true).first();
43562     },
43563     
43564     hiddenEl : function()
43565     {
43566         return this.el.select('input.hidden-number-input',true).first();
43567     }
43568     
43569 });/**
43570  * @class Roo.bootstrap.BezierSignature
43571  * @extends Roo.bootstrap.Component
43572  * Bootstrap BezierSignature class
43573  * This script refer to:
43574  *    Title: Signature Pad
43575  *    Author: szimek
43576  *    Availability: https://github.com/szimek/signature_pad
43577  *
43578  * @constructor
43579  * Create a new BezierSignature
43580  * @param {Object} config The config object
43581  */
43582
43583 Roo.bootstrap.BezierSignature = function(config){
43584     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43585     this.addEvents({
43586         "resize" : true
43587     });
43588 };
43589
43590 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43591 {
43592      
43593     curve_data: [],
43594     
43595     is_empty: true,
43596     
43597     mouse_btn_down: true,
43598     
43599     /**
43600      * @cfg {int} canvas height
43601      */
43602     canvas_height: '200px',
43603     
43604     /**
43605      * @cfg {float|function} Radius of a single dot.
43606      */ 
43607     dot_size: false,
43608     
43609     /**
43610      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43611      */
43612     min_width: 0.5,
43613     
43614     /**
43615      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43616      */
43617     max_width: 2.5,
43618     
43619     /**
43620      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43621      */
43622     throttle: 16,
43623     
43624     /**
43625      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43626      */
43627     min_distance: 5,
43628     
43629     /**
43630      * @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.
43631      */
43632     bg_color: 'rgba(0, 0, 0, 0)',
43633     
43634     /**
43635      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43636      */
43637     dot_color: 'black',
43638     
43639     /**
43640      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43641      */ 
43642     velocity_filter_weight: 0.7,
43643     
43644     /**
43645      * @cfg {function} Callback when stroke begin. 
43646      */
43647     onBegin: false,
43648     
43649     /**
43650      * @cfg {function} Callback when stroke end.
43651      */
43652     onEnd: false,
43653     
43654     getAutoCreate : function()
43655     {
43656         var cls = 'roo-signature column';
43657         
43658         if(this.cls){
43659             cls += ' ' + this.cls;
43660         }
43661         
43662         var col_sizes = [
43663             'lg',
43664             'md',
43665             'sm',
43666             'xs'
43667         ];
43668         
43669         for(var i = 0; i < col_sizes.length; i++) {
43670             if(this[col_sizes[i]]) {
43671                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43672             }
43673         }
43674         
43675         var cfg = {
43676             tag: 'div',
43677             cls: cls,
43678             cn: [
43679                 {
43680                     tag: 'div',
43681                     cls: 'roo-signature-body',
43682                     cn: [
43683                         {
43684                             tag: 'canvas',
43685                             cls: 'roo-signature-body-canvas',
43686                             height: this.canvas_height,
43687                             width: this.canvas_width
43688                         }
43689                     ]
43690                 },
43691                 {
43692                     tag: 'input',
43693                     type: 'file',
43694                     style: 'display: none'
43695                 }
43696             ]
43697         };
43698         
43699         return cfg;
43700     },
43701     
43702     initEvents: function() 
43703     {
43704         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43705         
43706         var canvas = this.canvasEl();
43707         
43708         // mouse && touch event swapping...
43709         canvas.dom.style.touchAction = 'none';
43710         canvas.dom.style.msTouchAction = 'none';
43711         
43712         this.mouse_btn_down = false;
43713         canvas.on('mousedown', this._handleMouseDown, this);
43714         canvas.on('mousemove', this._handleMouseMove, this);
43715         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43716         
43717         if (window.PointerEvent) {
43718             canvas.on('pointerdown', this._handleMouseDown, this);
43719             canvas.on('pointermove', this._handleMouseMove, this);
43720             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43721         }
43722         
43723         if ('ontouchstart' in window) {
43724             canvas.on('touchstart', this._handleTouchStart, this);
43725             canvas.on('touchmove', this._handleTouchMove, this);
43726             canvas.on('touchend', this._handleTouchEnd, this);
43727         }
43728         
43729         Roo.EventManager.onWindowResize(this.resize, this, true);
43730         
43731         // file input event
43732         this.fileEl().on('change', this.uploadImage, this);
43733         
43734         this.clear();
43735         
43736         this.resize();
43737     },
43738     
43739     resize: function(){
43740         
43741         var canvas = this.canvasEl().dom;
43742         var ctx = this.canvasElCtx();
43743         var img_data = false;
43744         
43745         if(canvas.width > 0) {
43746             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43747         }
43748         // setting canvas width will clean img data
43749         canvas.width = 0;
43750         
43751         var style = window.getComputedStyle ? 
43752             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43753             
43754         var padding_left = parseInt(style.paddingLeft) || 0;
43755         var padding_right = parseInt(style.paddingRight) || 0;
43756         
43757         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43758         
43759         if(img_data) {
43760             ctx.putImageData(img_data, 0, 0);
43761         }
43762     },
43763     
43764     _handleMouseDown: function(e)
43765     {
43766         if (e.browserEvent.which === 1) {
43767             this.mouse_btn_down = true;
43768             this.strokeBegin(e);
43769         }
43770     },
43771     
43772     _handleMouseMove: function (e)
43773     {
43774         if (this.mouse_btn_down) {
43775             this.strokeMoveUpdate(e);
43776         }
43777     },
43778     
43779     _handleMouseUp: function (e)
43780     {
43781         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43782             this.mouse_btn_down = false;
43783             this.strokeEnd(e);
43784         }
43785     },
43786     
43787     _handleTouchStart: function (e) {
43788         
43789         e.preventDefault();
43790         if (e.browserEvent.targetTouches.length === 1) {
43791             // var touch = e.browserEvent.changedTouches[0];
43792             // this.strokeBegin(touch);
43793             
43794              this.strokeBegin(e); // assume e catching the correct xy...
43795         }
43796     },
43797     
43798     _handleTouchMove: function (e) {
43799         e.preventDefault();
43800         // var touch = event.targetTouches[0];
43801         // _this._strokeMoveUpdate(touch);
43802         this.strokeMoveUpdate(e);
43803     },
43804     
43805     _handleTouchEnd: function (e) {
43806         var wasCanvasTouched = e.target === this.canvasEl().dom;
43807         if (wasCanvasTouched) {
43808             e.preventDefault();
43809             // var touch = event.changedTouches[0];
43810             // _this._strokeEnd(touch);
43811             this.strokeEnd(e);
43812         }
43813     },
43814     
43815     reset: function () {
43816         this._lastPoints = [];
43817         this._lastVelocity = 0;
43818         this._lastWidth = (this.min_width + this.max_width) / 2;
43819         this.canvasElCtx().fillStyle = this.dot_color;
43820     },
43821     
43822     strokeMoveUpdate: function(e)
43823     {
43824         this.strokeUpdate(e);
43825         
43826         if (this.throttle) {
43827             this.throttleStroke(this.strokeUpdate, this.throttle);
43828         }
43829         else {
43830             this.strokeUpdate(e);
43831         }
43832     },
43833     
43834     strokeBegin: function(e)
43835     {
43836         var newPointGroup = {
43837             color: this.dot_color,
43838             points: []
43839         };
43840         
43841         if (typeof this.onBegin === 'function') {
43842             this.onBegin(e);
43843         }
43844         
43845         this.curve_data.push(newPointGroup);
43846         this.reset();
43847         this.strokeUpdate(e);
43848     },
43849     
43850     strokeUpdate: function(e)
43851     {
43852         var rect = this.canvasEl().dom.getBoundingClientRect();
43853         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43854         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43855         var lastPoints = lastPointGroup.points;
43856         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43857         var isLastPointTooClose = lastPoint
43858             ? point.distanceTo(lastPoint) <= this.min_distance
43859             : false;
43860         var color = lastPointGroup.color;
43861         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43862             var curve = this.addPoint(point);
43863             if (!lastPoint) {
43864                 this.drawDot({color: color, point: point});
43865             }
43866             else if (curve) {
43867                 this.drawCurve({color: color, curve: curve});
43868             }
43869             lastPoints.push({
43870                 time: point.time,
43871                 x: point.x,
43872                 y: point.y
43873             });
43874         }
43875     },
43876     
43877     strokeEnd: function(e)
43878     {
43879         this.strokeUpdate(e);
43880         if (typeof this.onEnd === 'function') {
43881             this.onEnd(e);
43882         }
43883     },
43884     
43885     addPoint:  function (point) {
43886         var _lastPoints = this._lastPoints;
43887         _lastPoints.push(point);
43888         if (_lastPoints.length > 2) {
43889             if (_lastPoints.length === 3) {
43890                 _lastPoints.unshift(_lastPoints[0]);
43891             }
43892             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43893             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43894             _lastPoints.shift();
43895             return curve;
43896         }
43897         return null;
43898     },
43899     
43900     calculateCurveWidths: function (startPoint, endPoint) {
43901         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43902             (1 - this.velocity_filter_weight) * this._lastVelocity;
43903
43904         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43905         var widths = {
43906             end: newWidth,
43907             start: this._lastWidth
43908         };
43909         
43910         this._lastVelocity = velocity;
43911         this._lastWidth = newWidth;
43912         return widths;
43913     },
43914     
43915     drawDot: function (_a) {
43916         var color = _a.color, point = _a.point;
43917         var ctx = this.canvasElCtx();
43918         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43919         ctx.beginPath();
43920         this.drawCurveSegment(point.x, point.y, width);
43921         ctx.closePath();
43922         ctx.fillStyle = color;
43923         ctx.fill();
43924     },
43925     
43926     drawCurve: function (_a) {
43927         var color = _a.color, curve = _a.curve;
43928         var ctx = this.canvasElCtx();
43929         var widthDelta = curve.endWidth - curve.startWidth;
43930         var drawSteps = Math.floor(curve.length()) * 2;
43931         ctx.beginPath();
43932         ctx.fillStyle = color;
43933         for (var i = 0; i < drawSteps; i += 1) {
43934         var t = i / drawSteps;
43935         var tt = t * t;
43936         var ttt = tt * t;
43937         var u = 1 - t;
43938         var uu = u * u;
43939         var uuu = uu * u;
43940         var x = uuu * curve.startPoint.x;
43941         x += 3 * uu * t * curve.control1.x;
43942         x += 3 * u * tt * curve.control2.x;
43943         x += ttt * curve.endPoint.x;
43944         var y = uuu * curve.startPoint.y;
43945         y += 3 * uu * t * curve.control1.y;
43946         y += 3 * u * tt * curve.control2.y;
43947         y += ttt * curve.endPoint.y;
43948         var width = curve.startWidth + ttt * widthDelta;
43949         this.drawCurveSegment(x, y, width);
43950         }
43951         ctx.closePath();
43952         ctx.fill();
43953     },
43954     
43955     drawCurveSegment: function (x, y, width) {
43956         var ctx = this.canvasElCtx();
43957         ctx.moveTo(x, y);
43958         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43959         this.is_empty = false;
43960     },
43961     
43962     clear: function()
43963     {
43964         var ctx = this.canvasElCtx();
43965         var canvas = this.canvasEl().dom;
43966         ctx.fillStyle = this.bg_color;
43967         ctx.clearRect(0, 0, canvas.width, canvas.height);
43968         ctx.fillRect(0, 0, canvas.width, canvas.height);
43969         this.curve_data = [];
43970         this.reset();
43971         this.is_empty = true;
43972     },
43973     
43974     fileEl: function()
43975     {
43976         return  this.el.select('input',true).first();
43977     },
43978     
43979     canvasEl: function()
43980     {
43981         return this.el.select('canvas',true).first();
43982     },
43983     
43984     canvasElCtx: function()
43985     {
43986         return this.el.select('canvas',true).first().dom.getContext('2d');
43987     },
43988     
43989     getImage: function(type)
43990     {
43991         if(this.is_empty) {
43992             return false;
43993         }
43994         
43995         // encryption ?
43996         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43997     },
43998     
43999     drawFromImage: function(img_src)
44000     {
44001         var img = new Image();
44002         
44003         img.onload = function(){
44004             this.canvasElCtx().drawImage(img, 0, 0);
44005         }.bind(this);
44006         
44007         img.src = img_src;
44008         
44009         this.is_empty = false;
44010     },
44011     
44012     selectImage: function()
44013     {
44014         this.fileEl().dom.click();
44015     },
44016     
44017     uploadImage: function(e)
44018     {
44019         var reader = new FileReader();
44020         
44021         reader.onload = function(e){
44022             var img = new Image();
44023             img.onload = function(){
44024                 this.reset();
44025                 this.canvasElCtx().drawImage(img, 0, 0);
44026             }.bind(this);
44027             img.src = e.target.result;
44028         }.bind(this);
44029         
44030         reader.readAsDataURL(e.target.files[0]);
44031     },
44032     
44033     // Bezier Point Constructor
44034     Point: (function () {
44035         function Point(x, y, time) {
44036             this.x = x;
44037             this.y = y;
44038             this.time = time || Date.now();
44039         }
44040         Point.prototype.distanceTo = function (start) {
44041             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44042         };
44043         Point.prototype.equals = function (other) {
44044             return this.x === other.x && this.y === other.y && this.time === other.time;
44045         };
44046         Point.prototype.velocityFrom = function (start) {
44047             return this.time !== start.time
44048             ? this.distanceTo(start) / (this.time - start.time)
44049             : 0;
44050         };
44051         return Point;
44052     }()),
44053     
44054     
44055     // Bezier Constructor
44056     Bezier: (function () {
44057         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44058             this.startPoint = startPoint;
44059             this.control2 = control2;
44060             this.control1 = control1;
44061             this.endPoint = endPoint;
44062             this.startWidth = startWidth;
44063             this.endWidth = endWidth;
44064         }
44065         Bezier.fromPoints = function (points, widths, scope) {
44066             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44067             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44068             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44069         };
44070         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44071             var dx1 = s1.x - s2.x;
44072             var dy1 = s1.y - s2.y;
44073             var dx2 = s2.x - s3.x;
44074             var dy2 = s2.y - s3.y;
44075             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44076             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44077             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44078             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44079             var dxm = m1.x - m2.x;
44080             var dym = m1.y - m2.y;
44081             var k = l2 / (l1 + l2);
44082             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44083             var tx = s2.x - cm.x;
44084             var ty = s2.y - cm.y;
44085             return {
44086                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44087                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44088             };
44089         };
44090         Bezier.prototype.length = function () {
44091             var steps = 10;
44092             var length = 0;
44093             var px;
44094             var py;
44095             for (var i = 0; i <= steps; i += 1) {
44096                 var t = i / steps;
44097                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44098                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44099                 if (i > 0) {
44100                     var xdiff = cx - px;
44101                     var ydiff = cy - py;
44102                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44103                 }
44104                 px = cx;
44105                 py = cy;
44106             }
44107             return length;
44108         };
44109         Bezier.prototype.point = function (t, start, c1, c2, end) {
44110             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44111             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44112             + (3.0 * c2 * (1.0 - t) * t * t)
44113             + (end * t * t * t);
44114         };
44115         return Bezier;
44116     }()),
44117     
44118     throttleStroke: function(fn, wait) {
44119       if (wait === void 0) { wait = 250; }
44120       var previous = 0;
44121       var timeout = null;
44122       var result;
44123       var storedContext;
44124       var storedArgs;
44125       var later = function () {
44126           previous = Date.now();
44127           timeout = null;
44128           result = fn.apply(storedContext, storedArgs);
44129           if (!timeout) {
44130               storedContext = null;
44131               storedArgs = [];
44132           }
44133       };
44134       return function wrapper() {
44135           var args = [];
44136           for (var _i = 0; _i < arguments.length; _i++) {
44137               args[_i] = arguments[_i];
44138           }
44139           var now = Date.now();
44140           var remaining = wait - (now - previous);
44141           storedContext = this;
44142           storedArgs = args;
44143           if (remaining <= 0 || remaining > wait) {
44144               if (timeout) {
44145                   clearTimeout(timeout);
44146                   timeout = null;
44147               }
44148               previous = now;
44149               result = fn.apply(storedContext, storedArgs);
44150               if (!timeout) {
44151                   storedContext = null;
44152                   storedArgs = [];
44153               }
44154           }
44155           else if (!timeout) {
44156               timeout = window.setTimeout(later, remaining);
44157           }
44158           return result;
44159       };
44160   }
44161   
44162 });
44163
44164  
44165
44166